diff --git a/.gitignore b/.gitignore index 524f096..f0acbb7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + +.vscode/ +.idea/ \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..2990876 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3267 @@ +# 1. Overview of G2p connect - Registry +- Governments give money to people for various reasons like subsidies, pensions, scholarships, and emergency help. People can choose how they want to receive this money, like in cash, through their bank, on their phone, or with vouchers. + +- But, each government department has to set up its own system to check if people are eligible for the money, make sure transactions are real, and actually send the money. They have to talk to different departments to gather all the needed information, and this leads to a lot of duplicate work and problems. +- Simply putting all the data in one place doesn't work well because it can be a security risk and needs a lot of new systems. The usual approach of just putting things 'online' doesn't consider the real issues like politics, how people use technology, and the need for new ideas. +- A G2P DPI - Registry , is about creating a system that works together and respects privacy, security, and individual choices. The usual steps for giving money involve checking if people qualify, confirming their identity, and sending the money to their chosen method. +- To make this process better, G2P Connect suggests building a secure, decentralized system that different departments can customize. This way, they can share common elements, solve problems, and make the process more efficient. +- G2P Connect is an open-source project that helps different government agencies in a country work together to deliver digital payments from start to finish. +- The G2P transaction process involves an individual asking for money, providing their ID, and going through some security steps. The system checks if they qualify by looking at different government databases. + +## 2. G2p specifications +- G2P Connect API Specifications is a project that makes it easy for different systems to work together. It sets rules for how they should talk to each other +- The main goals of G2P Connect Specifications are to make sure systems can work seamlessly together and follow the rules set by the country. It also aims to be flexible, meaning it can adapt to existing standards and use common methods like OAuth2 for security. +- The message structure used in G2P Connect is like a package with a signature and a header. The header includes important information like the version, message ID, and what action is being taken. +- The specifications also allow easy integration of different data types and ensure secure communication through digital signatures and encryption. +- The focus is on standardizing core interfaces, acting as connectors between solutions and enabling countries to implement a variety of use cases. +- G2P Connect doesn't care how systems send messages; it can work with different methods like HTTPS, messaging events, or file exchanges. It also makes sure that the dates, times, and currency codes used in the messages are in a format that everyone can understand. +- In simple terms, G2P Connect API Specifications is like a rulebook that different systems follow to work together when giving money to people. It's flexible, secure, and makes sure everyone understands each other. + + +# 3. Overview / List of libraries +### 3.1 G2pc-core-lib - +- The G2pc-core-lib serves as a central hub for managing shared, reusable elements within the G2p specification +- This includes 80% of reusable features, DTOs, and configurations consistent across all data providers and consumers. +- The core library encapsulates below essential components - + - Redis cache configurations + - Unirest library settings + - Common constants + - G2p-specific DTOs and enums + - Common exception handling + - Security configurations + - Utility functions +- The DTOs outlined in this library correspond to the elements specified in the G2p specification’s endpoints. In essence, they represent the key components integral to the functionality described in the G2p standards. E.g - HeaderDTO , MessageDTO , ResponseDTO , etc. further details are listed in the technical overview. +- DTOs are constructed based on the OOPs principle of reusability. Attributes are shared between request bodies and responses are identified, and common elements are defined in parent DTOs. Any remaining specific attributes are then placed in corresponding child DTOs. These can be more explained by the below example. +E.g - HeaderDTO , RequestHeaderDTO and ResponseHeaderDTO , etc. + ![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/HeaderDTORelationship.png) +- This module declares functionalities for token-based authentication, digital signature, and securing messages through encryption using various algorithms. +- As per the requirement , these encryption and digital signature functions can be called in the below combination. + ![Alt text]( https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/Sign-encry-table.png "a title") +- Custom validation exception - + - JSON schemas have been used for validating both request and response components. + - Below Custom validation exceptions are defined + - G2pHttpException + - G2pcValidationException + - These exceptions are used to handle http requests errors which can be thrown from unirest endpoints. + +- Note - All these changes have been done on the basis of G2p specification. + + +### 3.2 G2pc-dp-core-lib +- The G2pc-dp-core-lib defines entities and services specific to data providers. +- It incorporates methods for constructing responses and managing requests within custom data provider services. +- This library encapsulates both an entity and repositories responsible for handling the data stored in tables dedicated to message tracking and transaction information. +- Also , service implementation manages the tracking of transactions, including saving request details, determining record counts, constructing search responses, and updating transaction statuses. +- It also includes building cache requests, validating request headers and messages against JSON schemas, and handling transaction tracking in both Redis and a database. +- It integrates with other services and provides detailed error handling and logging. Also constructing response DTOs, managing encryption and signatures, sending responses via HTTP, and handling tokens. + +### 3.3 G2pc-dc-core-lib +- The G2pc-dc-core-lib defines entities and services specific to data consumers. +- It incorporates methods for constructing requests and managing responses within custom data consumer services. +- This library defines functionality for building and sending requests in a secure manner, involving encryption, digital signatures, token management, and caching. +- Also provides functionality for updating a cache, validating response headers, and validating response messages against predefined JSON schemas. +- This service is basically to handle and process responses within a system. +- Below is the communication diagram of DP and DC - + + ![Alt text]( https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/DC-DP-communication.png "a title") + +# 4. Data Provider (DP) Implementation +- In DP implementation , as explained in Overview of libraries , dependency of G2pc-dp-core-lib needs to be added. +- Data provider is going to act as provider as well as consumer as. +- As shown in Figure 2 data provider needs to implement the end point and also make calls to the endpoint of the data consumer. +- Implementation explained in below point when it act like data provider - + 1. At first Data provider service needs to write the /search end-point. + 2. This endpoint will receive a requestString transferred by the data consumer. + 3. In this endpoint authentication also needs to be defined to ensure that the correct user is accessing the endpoint or not. + 4. Also need to make sure that the correct signature and valid message is received. + 5. This requestString will get validated as per g2p specification. Please refer to the link mentioned and image below. + ![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/search-endpoint-spec.png "a title") + 6. Once requestString gets validated data provider should save that data in redis cache and transaction data in db and send acknowledgement back to data consumer. Refer below sequence diagram for reference - + ![Alt text](/home/ttpl-rt-119/Documents/CDPI/G2P-Code/Git_hub/g2pc-registry/docs/src/images/dp-search-sequence-diagram.png) +- Implementation explained in below point when it act like data consumer - + 1. When it acts like a consumer , it needs to define a scheduler. Scheduler is nothing but a framework that allows you to schedule and execute tasks at specific intervals or times. + 2. In this scheduler , dp will check whether there is any data stored in pending status with a particular cache key corresponding to that data provider. + 3. If it gets data it will build the response data and the call /on-search endpoint is defined in the data consumer . + 4. Refer below for understanding of flow from dp to parent libraries. + ![Alt text](/home/ttpl-rt-119/Documents/CDPI/G2P-Code/Git_hub/g2pc-registry/docs/src/images/dp-scheduler-seq-diagram.png) + +# 5. How to create a Data Provider ? +1. Create a spring boot application with the latest spring-boot version , maven and Java 17. And Click on generate to download. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/spring_boot_dp_creation.png) +2. Extract the downloaded jar and open it in IDE. +3. Add below dependencies in pom.xml +```` + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + g2pc.dp.core.lib + g2pc-dp-core-library + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.postgresql + postgresql + 42.5.4 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springdoc + springdoc-openapi-ui + 1.6.15 + + + org.springframework.security + spring-security-web + 6.1.2 + + + com.auth0 + java-jwt + 4.4.0 + + + jakarta.validation + jakarta.validation-api + 3.0.2 + + + org.springframework.security + spring-security-config + 6.1.2 + + + org.springframework.boot + spring-boot-starter-validation + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + javax.servlet + jstl + 1.2 + + + org.springframework.boot + spring-boot-devtools + true + +```` +4. Create package structure shown below. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/dp-package-strcuture.png) +5. Add .p12 file for search and on-search. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/.p12-dp.png) +6. In the config package , create the ObjectMapperConfig.java class. +```` +@Configuration +public class ObjectMapperConfig { + @Bean + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + return objectMapper; + } +} + +```` +7. In the constants package , create Constants.java class. +```` +package g2pc.ref.farmer.regsvc.constants; +public class Constants { + private Constants() { + } + public static final String SEARCH_REQUEST_RECEIVED = "Search request received successfully"; + public static final String INVALID_RESPONSE = "Invalid Response received from server"; + public static final String CONFLICT = "CONFLICT"; + public static final String INVALID_AUTHORIZATION = "Invalid Authorization"; + public static final String CACHE_KEY_SEARCH_STRING = "request-farmer*"; + public static final String CACHE_KEY_STRING = "request-farmer-"; + public static final String CONFIGURATION_MISMATCH_ERROR = "Configurations are not matching "; + public static final String STATUS_CACHE_KEY_STRING = "status-request-farmer-"; + public static final String STATUS_CACHE_KEY_SEARCH_STRING = "status-request-farmer*"; +} + +```` +8. In the controller.rest package , create a RegistryController.java class. +```` +@RestController +@Slf4j +@RequestMapping(produces = "application/json") +@Tag(name = "Provider", description = "Provider APIs") +public class RegistryController { +} + +```` +9. In the service package , create the respective DP ResponseBuilderService.java interface. Refer below example. +```` +public interface FarmerResponseBuilderService { + RegRecordFarmerDTO getRegRecordFarmerDTO(FarmerInfoEntity farmerInfoEntity); + } +```` +10. In the service package , create the respective ValidationService.java interface. Refer below example. +```` +public interface FarmerValidationService { + void validateRequestDTO (RequestDTO requestDTO) throws Exception; + RequestMessageDTO signatureValidation(Map metaData, RequestDTO requestDTO ) throws Exception; +} + +```` +11. In the serviceImpl package create implemented classes of above interfaces for respective dps. +```` +@Service +@Slf4j +public class FarmerResponseBuilderServiceImpl implements FarmerResponseBuilderService { + +} +```` +```` +@Service +@Slf4j +public class FarmerValidationServiceImpl implements FarmerValidationService { + } +```` +12. Add below autowired dependencies in the RegistryController class. +```` + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + FarmerValidationService farmerValidationService; + + @Autowired + private DpCommonUtils dpCommonUtils; + + @Autowired + private MsgTrackerRepository msgTrackerRepository; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private DpSftpPushUpdateService dpSftpPushUpdateService; + +```` +13. Add below application.yml and update as per below instructions. +```` +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + + datasource: + driverClassName: org.postgresql.Driver + # Add db name, schema name, username and password for db connection as per your postgres/mysql connection. + url: jdbc:postgresql://{Domain-id}}:{port}/{Database name}?currentSchema={Schema name} + username: {username of database connection} + password: {password of database connection} + + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: {add port as per requirement} + error: + include-message: always + +# This is redis connection configuration +spring.data.redis: + repositories.enabled: false + host: {host domain of redis connection} + password: {password of redis port} + port: {port of redis connection} + +# These are dc side url. Add respective host name e.g. - localhost and port e.g. - 8080 +client: + api_urls: + client_search_api: http://{host}:{port}/private/api/v1/registry/on-search + client_status_api: http://{host}:{port}/private/api/v1/registry/on-status + +# Below are Keycloak configuration. +keycloak: + # These configurations are given by dc that is the reason name is from_dc. + from_dc: + # url is for creating token , add domain name e.g. - http://127.0.0.1:8081/ , it means 8081 is port on which keycloak is running. + url: "https://{domain of dc instance}/auth/realms/data-consumer/protocol/openid-connect/token" + clientId: {client name given to client created in dc instance} + clientSecret: {client secret of client from dc keycloak instance. Refer 8.7 for same} + dp: + # These are dp keycloak instance url , admin name and password which is given while creating instance. + url: https://{domain of dp instance}/auth + username: {username of dp instance} + password: {password of dp instance} + master: + # master token url for particular dp keycloak, add domain name http://127.0.0.1:8081/ + url: https://{domain}/auth/realms/master/protocol/openid-connect/token + getClientUrl: https://{domain}/auth/admin/realms/{client id of dp}/clients + clientId: {client name given to admin client created in dp instance} + clientSecret: {client secret of admin client from dp keycloak instance. Refer 8.7 for same} + client: + # dp client token url. Add domain name and realm-id + url: https://{domain}/auth/realms/{realm-id}/protocol/openid-connect/token + realm: {realm id} + clientId: {dp client id} + clientSecret: {client secret of client from dp keycloak instance. Refer 8.7 for same} + +# Below configuration is for cryptography setting. +crypto: + # to_dc means these configurations used for on-search communication. + to_dc: + # flag of encryption (use only small case) + support_encryption: true + # flag of signature (use only small case) + support_signature: true + password: {password of on-search .p12 file} + key_path: {keypath of on-search .p12 file} + id: {dp id which will be common between dc and dp} + from_dc: + # flag of encryption (use only small case) + support_encryption: true + # flag of signature (use only small case) + support_signature: true + password: {password of search .p12 file which will be given from dc} + key_path: {keypath of search .p12 file which will be given from dc} + +dashboard: + dp_dashboard_url: "http://{domain}:{port}/d-solo/e62ae08b-a6e1-4095-af79-c36f02b8fae2/dp1-dashboard?orgId=1&refresh=5s&from=1701984074137&to=1702005674137&panelId=1" + +# sftp connection configurations. +sftp: + listener: + # hostname of sftp connection of dp e.g. localhost + host: {host name} + port: {port of sftp connection} + user: {username of sftp connection} + password: {password of sftp connection} + remote: + # path of sftp client for inbound e.g. /inbound + inbound_directory: {path mentioned in sftp client for inbound} + outbound_directory: {path mentioned in sftp client for outbound} + local: + inbound_directory: {path created in local machine for inbound} + outbound_directory: {path created in local machine for inbound} + + dc: + # hostname of sftp connection of dc e.g. localhost + host: {host name} + port: {port of sftp connection} + user: {username of sftp connection} + password: {password of sftp connection} + remote: + outbound_directory: {path mentioned in sftp client for outbound} + +sunbird: + enabled: true + elasticsearch: + host: {domain of elasticsearch like if running on local env i.e localhost} + port: {port mention in docker-compose for elastic search} + scheme: http +```` +14. Define below endpoint in RegistryController. +```` +@Operation(summary = "Receive search request") +@ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) +@PostMapping("/private/api/v1/registry/search") +public AcknowledgementDTO handleRequest(@RequestBody String requestString) throws Exception { + +```` +15. Add below code in the same method to add subtype in objectMapper to convert String in requestDTO in handleRequest(). +```` +ObjectMapper objectMapper = new ObjectMapper(); +objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + +RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); +RequestMessageDTO messageDTO = null; +```` +16. Add below code snippet handleRequest() to validate signature and encryption. +```` +Map metaData = (Map) requestDTO.getHeader().getMeta().getData(); +messageDTO = farmerValidationService.signatureValidation(metaData, requestDTO); +requestDTO.setMessage(messageDTO); +```` +17. Add below code snippet in handleRequest() to validate requestDTO as per g2p specifications and build cache request for Request string. In this buildCacheRequest it has already been defined in parent libraries , just need to call. +```` +String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); +try { + farmerValidationService.validateRequestDTO(requestDTO); + return requestHandlerService.buildCacheRequest( + objectMapper.writeValueAsString(requestDTO), cacheKey, CoreConstants.SEND_PROTOCOL_HTTPS); +} catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); +} +catch (JsonProcessingException e){ + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR , e.getMessage()); +} +catch (Exception e){ + throw new ResponseStatusException(HttpStatus.BAD_REQUEST , e.getMessage()); +} + +```` +18. Add below 2 methods Custom Exception handling using spring boot annotations in RegistryController. +```` +@ExceptionHandler(value + = G2pcValidationException.class) +@ResponseStatus(HttpStatus.BAD_REQUEST) +public ValidationErrorResponse +handleValidationException( + G2pcValidationException ex) { + return new ValidationErrorResponse( + ex.getG2PcErrorList()); +} + +@ExceptionHandler(value + = G2pHttpException.class) +@ResponseStatus(HttpStatus.UNAUTHORIZED) +public ErrorResponse handleG2pHttpStatusException( + G2pHttpException ex) { + return new ErrorResponse(ex.getG2PcError()); + } + +```` +19. Create below endpoint for clearing the db. +```` + @GetMapping("/public/api/v1/registry/clear-db") + public void clearDb() throws G2pHttpException, IOException { + dpCommonUtils.handleToken(); + msgTrackerRepository.deleteAll(); + log.info("DP-1 DB cleared"); + Set keys = redisTemplate.keys("*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + log.info("DP-1 Redis cache cleared"); + } +```` +20. Create below class DcSftpListener for handing sftp request. +```` +package g2pc.ref.farmer.regsvc.controller.sftp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.ref.farmer.regsvc.constants.Constants; +import g2pc.ref.farmer.regsvc.dto.SftpDpData; +import g2pc.ref.farmer.regsvc.service.DpSftpPushUpdateService; +import g2pc.ref.farmer.regsvc.service.FarmerValidationService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.web.server.ResponseStatusException; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +@Configuration +@Slf4j +public class DcSftpListener { + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpLocalDirectoryInbound; + + @Value("${sftp.listener.local.outbound_directory}") + private String sftpLocalDirectoryOutbound; + + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + FarmerValidationService farmerValidationService; + + @Autowired + private DpSftpPushUpdateService dpSftpPushUpdateService; + + @SuppressWarnings("unchecked") + @ServiceActivator(inputChannel = "sftpInbound") + public void handleMessageInbound(Message message) { + try { + File file = message.getPayload(); + log.info("Received Message from inbound directory of dp-1: {}", file.getName()); + if (ObjectUtils.isNotEmpty(file) && file.getName().contains(".json")) { + String requestString = new String(Files.readAllBytes(file.toPath())); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); + RequestMessageDTO messageDTO; + + Map metaData = (Map) requestDTO.getHeader().getMeta().getData(); + + messageDTO = farmerValidationService.signatureValidation(metaData, requestDTO); + requestDTO.setMessage(messageDTO); + String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); + try { + farmerValidationService.validateRequestDTO(requestDTO); + requestHandlerService.buildCacheRequest( + objectMapper.writeValueAsString(requestDTO), cacheKey, CoreConstants.SEND_PROTOCOL_SFTP); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } + Files.deleteIfExists(Path.of(sftpLocalDirectoryInbound + "/" + file.getName())); + } catch (Exception e) { + log.error("Error: ", e); + } + } + @ServiceActivator(inputChannel = "errorChannel") + public void handleError(Message message) { + Throwable error = (Throwable) message.getPayload(); + log.error("Handling ERROR: {}", error.getMessage()); + } +} +```` +21. Create Query and Query param dto for data provider requirement in dto.request package. Below are examples. +```` +@Getter +@Setter +@ToString +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class QueryFarmerDTO { + + + @JsonProperty("query_params") + private QueryParamsFarmerDTO queryParams; +} +```` +```` +@Getter +@Setter +@ToString +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class QueryParamsFarmerDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("season") + private String season; +} + +```` +22. Create regRecordDTO for data provider as per on search endpoint requirement in dto.response package shown below. +```` +@Getter +@Setter +@ToString +@NoArgsConstructor +public class DataFarmerDTO { + + @JsonProperty("reg_records") + private List regRecords; +} +```` +```` +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class RegRecordFarmerDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("farmer_name") + private String farmerName; + + @JsonProperty("season") + private String season; + + @JsonProperty("payment_status") + private String paymentStatus; + + @JsonProperty("payment_date") + private String paymentDate; + + @JsonProperty("payment_amount") + private Double paymentAmount; +} + +```` +23. To get data provider information create a data-provider info table in db , entity and repository as shown below. +```` +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "farmer_info") +public class FarmerInfoEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + private String farmerId; + + private String farmerName; + + private String season; + + private String paymentStatus; + + private String paymentDate; + + private Double paymentAmount; + +```` +24. Write your respective method using data jpa concept. +```` +@Repository +public interface FarmerInfoRepository extends JpaRepository { + Optional findBySeasonAndFarmerId(String season, String farmerId); +} +```` +25. Define below method in ValidationServiceImpl as it has implemented from ValidationService interface. +```` +@Override +public RequestMessageDTO signatureValidation(Map metaData, RequestDTO requestDTO) throws Exception { +```` +26. Define below autowired beans and configurations in ValidationServiceImpl. +```` + @Autowired + RequestHandlerService requestHandlerService; + + @Value("${crypto.from_dc.support_encryption}") + private boolean isEncrypt; + + @Value("${crypto.from_dc.support_signature}") + private boolean isSign; + + @Value("${crypto.from_dc.password}") + private String p12Password; + + @Autowired + private AsymmetricSignatureService asymmetricSignatureService; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Autowired + private ResourceLoader resourceLoader; + + @Value("${crypto.from_dc.key.path}") + private String farmer_key_path; +```` +27. Add below code snippet in signatureValidation method. This is the validation for signature and encryption to check whether this is transferred correctly. + 1. Check isSign flag , if yes check metadata given from controller is true or false , if not true throw error that configurations are not valid. + 2. Take farmerKeyPath for dp .p12 file. + 3. Check isEncrypt flag , if true check isMsgEncrypt flag from header is true or false , if not true throw error that configurations are not valid. + 4. Recreate signature using header and message. Verify both signature if error from verifySignature throw error signature invalid. + 5. Decrypt message , if successfully decrypted add in requestDto. + 6. Above logic is same in 4 cases of signature and encryption mentioned "Overview / List of libraries". +```` + ObjectMapper objectMapper = new ObjectMapper(); + RequestMessageDTO messageDTO; + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(farmer_key_path); + InputStream fis = resource.getInputStream(); + if(isEncrypt){ + if(!requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = requestDTO.getMessage().toString(); + String data = requestHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ->"+e.getMessage())); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(requestDTO.getHeader().getIsMsgEncrypted()){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(RequestMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = requestHeaderString+messageString; + log.info("Signature ->"+requestSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = requestDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(RequestMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + } + } + requestDTO.setMessage(messageDTO); + return messageDTO; + +```` +28. Implement below method from ValidationService in ValidationServiceImpl. +```` +@Override +public void validateRequestDTO(RequestDTO requestDTO) throws G2pcValidationException, IOException { +```` +29. Change QueryFarmerDTO , QueryParamsFarmerDTO , with respective data provider DTOs. In this method , validations of message and header methods are called from parent dp-core. +```` +ObjectMapper objectMapper = new ObjectMapper(); +objectMapper.registerSubtypes(QueryDTO.class, + QueryFarmerDTO.class, QueryParamsFarmerDTO.class); +byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); +RequestMessageDTO messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); +String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(requestDTO.getHeader()); +RequestHeaderDTO headerDTO = objectMapper.readerFor(RequestHeaderDTO.class). + readValue(headerString); +requestHandlerService.validateRequestHeader(headerDTO); +requestHandlerService.validateRequestMessage(messageDTO); +```` +30. In Scheduler class define below autowired bean. these beans are from dp-core library and also custom created in dp. +```` + @Autowired + private ResponseBuilderService responseBuilderService; + + @Autowired + private FarmerResponseBuilderService farmerResponseBuilderService; + + @Autowired + private TxnTrackerRedisService txnTrackerRedisService; + + @Autowired + private TxnTrackerDbService txnTrackerDbService; +```` +Define below method in Scheduler. +```` +@Scheduled(cron = "0 */1 * ? * *")// runs every 1 min. +@Transactional +public void responseScheduler() throws IOException { +```` +31. Define try catch in the same method. +32. Add below snippet in method responseScheduler(). +```` +ObjectMapper objectMapper = new ObjectMapper(); +objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); +```` +33. Call method from txnTrackerRedisService to get cache list. +```` +List cacheKeysList = txnTrackerRedisService.getCacheKeys(Constants.CACHE_KEY_SEARCH_STRING); +```` +34. Check whether in list the status in PNDG or not. +```` +if (cacheDTO.getStatus().equals(HeaderStatusENUM.PDNG.toValue())) { +```` +35. Add below code snippet to in FarmerResponseBuilderServiceImpl. +```` +@Autowired +private FarmerInfoRepository farmerInfoRepository; + + +/** +* Get farmer records information from DB +* +* @param farmerInfoEntity required +* @return Farmer records +*/ +@Override +public RegRecordFarmerDTO getRegRecordFarmerDTO(FarmerInfoEntity farmerInfoEntity) { + RegRecordFarmerDTO dto = new RegRecordFarmerDTO(); + dto.setFarmerId(farmerInfoEntity.getFarmerId()); + dto.setFarmerName(farmerInfoEntity.getFarmerName()); + dto.setSeason(farmerInfoEntity.getSeason()); + dto.setPaymentStatus(farmerInfoEntity.getPaymentStatus()); + dto.setPaymentDate(farmerInfoEntity.getPaymentDate()); + dto.setPaymentAmount(farmerInfoEntity.getPaymentAmount()); + return dto; +} + +/** +* Get farmer records information string +* +* @param queryDTOList required +* @return List of farmer records +*/ +@Override +public List getRegFarmerRecords(List queryDTOList) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + List regFarmerRecordsList = new ArrayList<>(); + for (QueryDTO queryDTO : queryDTOList) { + String queryParams = objectMapper.writeValueAsString(queryDTO.getQueryParams()); + QueryParamsFarmerDTO queryParamsFarmerDTO = objectMapper.readValue(queryParams, QueryParamsFarmerDTO.class); + String farmerId = queryParamsFarmerDTO.getFarmerId(); + String season = queryParamsFarmerDTO.getSeason(); + Optional optional = farmerInfoRepository.findBySeasonAndFarmerId(season, farmerId); + if (optional.isPresent()) { + RegRecordFarmerDTO regRecordFarmerDTO = getRegRecordFarmerDTO(optional.get()); + regFarmerRecordsList.add(objectMapper.writeValueAsString(regRecordFarmerDTO)); + } else { + regFarmerRecordsList.add(StringUtils.EMPTY); + } + } + return regFarmerRecordsList; + +```` +36. Add below snippet in scheduler class method responseScheduler() if condition. +```` + { + String protocol = cacheDTO.getProtocol(); + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class).readValue(cacheDTO.getData()); + MsgTrackerEntity msgTrackerEntity = txnTrackerDbService.saveRequestDetails(requestDTO, protocol); + List queryDTOList = msgTrackerEntity.getTxnTrackerEntityList().stream() + .map(txnTrackerEntity -> { + try { + return objectMapper.readValue(txnTrackerEntity.getQuery(), QueryDTO.class); + } catch (JsonProcessingException e) { + return null; + } + }).toList(); + List refRecordsStringsList = farmerResponseBuilderService.getRegFarmerRecords(queryDTOList); + G2pcError g2pcError = responseBuilderService.buildOnSearchScheduler(refRecordsStringsList , cacheDTO); + log.info("on-search database updation response from sunbird - "+g2pcError.getCode()); + if (!g2pcError.getCode().equals(HttpStatus.OK.toString())) { + throw new G2pHttpException(g2pcError); + } else { + txnTrackerRedisService.updateRequestDetails(cacheKey, HeaderStatusENUM.SUCC.toValue(), cacheDTO); + } + + } +```` +37. Add below the catch statement at last as mentioned in point 33 that try is already written. +```` +catch ( G2pHttpException e){ + log.error("Exception thrown from on-search endpoint"+ e.getG2PcError().getMessage()); +} +catch (Exception ex) { + log.error("Exception in responseScheduler: {}", ex.getMessage()); +} + +```` +38. This scheduler will run every 1 min. But to test this code write a test case in Test class. +```` +@Test +void testResponseScheduler() throws IOException { + scheduler.responseScheduler(); +} +```` +39. Create new method in RegistryController for /status +```` + @Operation(summary = "Receive status request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/registry/txn/status") + public AcknowledgementDTO handleStatusRequest(@RequestBody String requestString) throws Exception { + } +```` +40. Add below code for authenticating user +```` +dpCommonUtils.handleToken(); +```` +41. Add below code for validating the statusRequest and updating cache. +```` + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class, HeaderDTO.class); + + StatusRequestDTO statusRequestDTO = objectMapper.readerFor(StatusRequestDTO.class). + readValue(requestString); + StatusRequestMessageDTO statusRequestMessageDTO = null; + + Map metaData = (Map) statusRequestDTO.getHeader().getMeta().getData(); + + statusRequestMessageDTO = farmerValidationService.signatureValidation(metaData, statusRequestDTO); + statusRequestDTO.setMessage(statusRequestMessageDTO); + String cacheKey = Constants.STATUS_CACHE_KEY_STRING + statusRequestMessageDTO.getTransactionId(); + try { + farmerValidationService.validateStatusRequestDTO(statusRequestDTO); + return requestHandlerService.buildCacheStatusRequest( + objectMapper.writeValueAsString(statusRequestDTO), cacheKey,CoreConstants.SEND_PROTOCOL_HTTPS); + } + catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + }catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + +```` +42. Add below endpoint in RegistryController.java. +```` + @GetMapping(value = "/dashboard/sftp/dp1/data", produces = "text/event-stream") + public SseEmitter sseEmitterFirstPanel() { + return dpSftpPushUpdateService.register(); + } +```` +43. Add below overloaded method in FarmerValidationService +```` +StatusRequestMessageDTO signatureValidation(Map metaData, StatusRequestDTO requestDTO) throws Exception ; +```` +44. Override above method in FarmerValidationServiceImpl. +```` + @Override + public StatusRequestMessageDTO signatureValidation(Map metaData, StatusRequestDTO requestDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + StatusRequestMessageDTO messageDTO; + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(farmer_key_path); + InputStream fis = resource.getInputStream(); + if(isEncrypt){ + if(!requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = requestDTO.getMessage().toString(); + String data = requestHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ->"+e.getMessage())); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(requestDTO.getHeader().getIsMsgEncrypted()){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusRequestMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = requestHeaderString+messageString; + log.info("Signature ->"+requestSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = requestDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusRequestMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(requestDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + } + } + requestDTO.setMessage(messageDTO); + return messageDTO; + } +```` +45. Add below method to validated statusRequestMessage in FarmerValidationService. +```` +void validateStatusRequestDTO (StatusRequestDTO requestDTO) throws IOException, G2pcValidationException; +```` +46. Override above method in FarmerValidationServiceImpl +```` +@Override + public void validateStatusRequestDTO(StatusRequestDTO statusRequestDTO) throws IOException, G2pcValidationException { + ObjectMapper objectMapper = new ObjectMapper(); + byte[] json = objectMapper.writeValueAsBytes(statusRequestDTO.getMessage()); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(statusRequestDTO.getHeader()); + RequestHeaderDTO headerDTO = objectMapper.readerFor(RequestHeaderDTO.class). + readValue(headerString); + requestHandlerService.validateRequestHeader(headerDTO); + requestHandlerService.validateStatusRequestMessage(statusRequestMessageDTO); + } +```` +47. To call on-status endpoint from dc add below snippet in scheduler class in try block. Refer application zip. + 1. Get cache key from redis stored with start of key "status-request-farmer*". + 2. Iterate list of cache. + 3. Get request data for particular cache key and convert it into cacheDTO. + 4. Check if status is pending for that data. + 5. Get StatusRequestDto from cacheDto and statusRequestMessageDTO from StatusRequestDto. + 6. Fetch the msgTrackerEntity from db using statusRequestDto and build responseHeaderDto. + 7. Build StatusResponseMessageDto using StatusRequestMessageDto. + 8. Build StatusResponseString , create resource using farmer key path and send response to dc. +```` + List statusCacheKeysList = txnTrackerRedisService.getCacheKeys(Constants.STATUS_CACHE_KEY_SEARCH_STRING); + for (String cacheKey : statusCacheKeysList) { + String requestData = txnTrackerRedisService.getRequestData(cacheKey); + CacheDTO cacheDTO = objectMapper.readerFor(CacheDTO.class).readValue(requestData); + if (cacheDTO.getStatus().equals(HeaderStatusENUM.PDNG.toValue())) { + StatusRequestDTO statusRequestDTO = objectMapper.readerFor(StatusRequestDTO.class).readValue(cacheDTO.getData()); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.convertValue(statusRequestDTO.getMessage(), StatusRequestMessageDTO.class); + G2pcError g2pcError = responseBuilderService.buildOnStatusScheduler(cacheDTO); + log.info("on-status database updation response from sunbird - "+g2pcError.getCode()); + if (!g2pcError.getCode().equals(HttpStatus.OK.toString())) { + throw new G2pHttpException(g2pcError); + } else { + txnTrackerDbService.updateMessageTrackerStatusDb(statusRequestMessageDTO.getTransactionId()); + txnTrackerRedisService.updateRequestDetails(cacheKey, HeaderStatusENUM.SUCC.toValue(), cacheDTO); + } + } + } +```` +48. Create DpDashboardController for creating dashboard endpoints for Grafana. +```` +package g2pc.ref.farmer.regsvc.controller.rest; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class DpDashboardController { + + @Value("${dashboard.dp_dashboard_url}") + private String dpDashboardUrl; + + @GetMapping("/dashboard") + public String showDashboardPage(Model model) { + model.addAttribute("dp_dashboard_url", dpDashboardUrl); + return "dashboard"; + } +} + +```` +49. Add CorsConfig.java in config folder, refer below code. +```` +package g2pc.ref.farmer.regsvc.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Value("${dashboard.cors_origin_url}") + private String corsOriginUrl; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins(corsOriginUrl) + .allowedMethods("*") + .allowedHeaders("*"); + } +} +```` +50. Add below dto SftpDpData in DP. +```` +package g2pc.ref.farmer.regsvc.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SftpDpData { + + private String dpType; + private String messageTs; + private String transactionId; + private String fileName; +} + +```` +51. Add below interface in service package +```` +package g2pc.ref.farmer.regsvc.service; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +public interface DpSftpPushUpdateService { + + SseEmitter register(); + + void pushUpdate(Object update); +} +```` +52. Implement service in below class. +```` +package g2pc.ref.farmer.regsvc.serviceimpl; + +import g2pc.ref.farmer.regsvc.dto.SftpDpData; +import g2pc.ref.farmer.regsvc.service.DpSftpPushUpdateService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class DpSftpPushUpdateServiceImpl implements DpSftpPushUpdateService { + private final List emitters = new ArrayList<>(); + public SseEmitter register() { + int minutes = 15; + long timeout = (long) minutes * 60000; + SseEmitter emitter = new SseEmitter(timeout); + this.emitters.add(emitter); + emitter.onCompletion(() -> this.emitters.remove(emitter)); + emitter.onTimeout(() -> this.emitters.remove(emitter)); + log.info("SSE emitter registered" + emitter); + return emitter; + } + public void pushUpdate(Object update) { + List deadEmitters = new ArrayList<>(); + this.emitters.forEach(emitter -> { + try { + emitter.send(update); + } catch (IOException e) { + deadEmitters.add(emitter); + } + }); + this.emitters.removeAll(deadEmitters); + } + public SftpDpData getSftpDpData(String dpType, String messageTs, String transactionId, String filename){ + SftpDpData sftpDpData = new SftpDpData(); + sftpDpData.setDpType(dpType); + sftpDpData.setMessageTs(messageTs); + sftpDpData.setTransactionId(transactionId); + sftpDpData.setFileName(filename); + return sftpDpData; + } +} + +```` + +# 6. Data Consumer (DC) Implementation +- In DC implementation , as explained in Overview of libraries , dependency of G2pc-dc-core-lib needs to be added. +- Data consumers are going to act as consumers as well as providers. +Implementation explained in below point when it act like data consumer - + 1. When it acts like a consumer , it needs to define an endpoint which accepts payload . Example Shown in the image below. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/patload_postman.png) + 2. Using this data consumer will decide which data provider’s endpoint it needs to call and which request it needs to build. + 3. Once a request is created /search endpoint it will call and once positive acknowledgement is there it will save pending status in cache for particular transaction id. Refer below for more understanding. + ![Alt text](/home/ttpl-rt-119/Documents/CDPI/G2P-Code/Git_hub/g2pc-registry/docs/src/images/dc-seq-diagram-2.png) +- Implementation explained in below point when it act like data provider - + 1. As shown in Figure 2 data provider needs to implement the end point and also make calls to the endpoint of the data provider. + 2. At first Data consumer service needs to write the /on-search end-point. + 3. This endpoint will receive a responseString transferred by the data provider. + 4. In this endpoint authentication also needs to be defined to ensure that the correct user is accessing the endpoint or not. + 5. Also need to make sure that the correct signature and valid message is received. + 6. This responseString will get validated as per g2p specification. Please refer to the link mentioned and image below. + ![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/on_search_spec.png) + 7. Once responseString gets validated data consumers should update that data in redis cache and send acknowledgement back to the data consumer. Refer below for more understanding. + ![Alt text](/home/ttpl-rt-119/Documents/CDPI/G2P-Code/Git_hub/g2pc-registry/docs/src/images/dc-on-search-sequence-dia.png) + +# 7. How to create a Data Consumer ? +1. Create a spring boot application with the latest spring-boot version , maven and Java 17. And Click on generate to download. + ![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/spring_boot_dc_creation.png) +2. Extract the downloaded jar and open it in IDE. +3. Add below dependencies in tag in pom.xml +```` + + org.springframework.boot + spring-boot-starter + + + org.projectlombok + lombok + true + + + org.postgresql + postgresql + 42.5.4 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + g2pc.dc.core.lib + g2pc-dc-core-library + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.springdoc + springdoc-openapi-ui + 1.6.15 + + + com.networknt + json-schema-validator + 1.0.82 + + + jakarta.validation + jakarta.validation-api + 2.0.2 + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + javax.servlet + jstl + 1.2 + + + org.springframework.boot + spring-boot-devtools + true + + + javax.servlet.jsp.jstl + javax.servlet.jsp.jstl-api + 1.2.1 + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + 7.10.2 + +```` +4. Create package structure shown below. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/dc-package-structure.png) +5. Add .p12 files for search received from dp and on-search +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/.p12-dc.png) +6. In the config package , create the ObjectMapperConfig.java class. This class is used to avoid ambiguity between parent class and child class of Header. +```` +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +@Configuration +public class ObjectMapperConfig { + @Bean + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class); + return objectMapper; + } +} +```` +7. Take reference of below application.yml , create application.yml for particular dc with the help of details mentioned after .yml file. +```` +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + + datasource: + driverClassName: org.postgresql.Driver + # Add db name, schema name, username and password for db connection as per your postgres/mysql connection. + url: jdbc:postgresql://{Domain-id}}:{port}/{Database name}?currentSchema={Schema name} + username: {username of database connection} + password: {password of database connection} + + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: {add port as per requirement} + error: + include-message: always + +# This is redis connection configuration +spring.data.redis: + repositories.enabled: false + host: {host domain of redis connection} + password: {password of redis port} + port: {port of redis connection} + +keycloak: + # These configurations are given by dc that is the reason name is from_dp. + from_dp: + # this will be many dp , add url , clientid and client secret for every dp. + {dp-name}: + url: "https://{domain of dc instance}/auth/realms/{realm name of dp}/protocol/openid-connect/token" + clientId: {client id of dp given by dp} + clientSecret: {client secret of dp client} + dc: + url: https://{domain of dc instance}/auth + username: {username of dc instance} + password: {password of dc instance} + master: + # master token url for particular dc keycloak, add domain name http://127.0.0.1:8081/ + url: https://{domain}/auth/realms/master/protocol/openid-connect/token + getClientUrl: https://{domain}/auth/admin/realms/{client id of dp}/clients + clientId: {client name given to admin client created in dc instance} + clientSecret: {client secret of admin client from dc keycloak instance. Refer 8.7 for same} + client: + # dc client token url. Add domain name and realm-id + url: https://{domain}/auth/realms/{realm-id}/protocol/openid-connect/token + realm: {realm id} + clientId: {dc client id} + clientSecret: {client secret of client from dc keycloak instance. Refer 8.7 for same} + +crypto: + to_dp_{dp_name}: + # flag of encryption (use only small case) + support_encryption: true + # flag of signature (use only small case) + support_signature: true + password: {password of search .p12 file of particuler dp} + key_path: {keypath of search .p12 file of particuler dp} + from_dp_{dp_name}: + # flag of encryption (use only small case) + support_encryption: true + # flag of signature (use only small case) + support_signature: true + password: {password of on-search .p12 file} + key_path: {keypath of on-search .p12 file} + id: {dp id which will be common between dc and dp} + +registry: + api_urls: + {dp-name}_search_api: "http://{host}:{port}/private/api/v1/registry/search" + {dp-name}_status_api: "http://{host}:{port}/private/api/v1/registry/txn/status" + +dashboard: + left_panel_url: "https://{domain of dc}/grafana/d-solo/cb26f39f-97f3-43ea-9f42-68d49d9822a3/left-panel-data?orgId=1&refresh=5s&from=1701984074137&to=1702005674137&panelId=1" + right_panel_url: "https://{domain of dc}/grafana/d-solo/d9f9c625-934b-4a65-995f-c742daad6387/right-panel-data?orgId=1&refresh=5s&from=1701984074137&to=1702005674137&panelId=1" + bottom_panel_url: "https://{domain of dc}/grafana/d-solo/a25a6c65-fda7-4fdd-80a7-80442aed17e8/bottom-panel-data?orgId=1&refresh=5s&from=1701984074137&to=1702005674137&panelId=1" + post_endpoint_url: "http://localhost:8000/public/api/v1/consumer/search/csv" + clear_dc_db_endpoint_url: "http://{host}:{port of dc}/private/api/v1/registry/clear-db" + clear_dp1_db_endpoint_url: "http://{host}:{port of dp1}/private/api/v1/registry/clear-db" + # Add all respective clear-db endpoint of all dp example is above. + left_panel_data_endpoint_url: "http://{host}:{port of dc}/dashboard/leftPanel/data" + sftp_post_endpoint_url: "http://{host}:{port of dc}/public/api/v1/consumer/search/sftp/csv" + sftp_dc_data_endpoint_url: "http://{host}:{port of dc}/dashboard/sftp/dc/data" + sftp_dp1_data_endpoint_url: "http://{host}:{port of dp1}/dashboard/sftp/dp1/data" + # Add all respective sftp_dp1_data_endpoint_url endpoint of all dp example is above. + dc_status_endpoint_url: "http://{host}:{port of dc}/private/api/v1/consumer/status/payload?transactionType=search&transactionId=" + sftp_left_panel_url: "https://{domain of dc}/grafana/d-solo/aa62b4d5-f0c6-4c5d-97eb-753343c89a32/sftp-left-panel-data?orgId=1&refresh=5s&from=1705573550702&to=1705595150703&panelId=1" + sftp_right_panel_url: "https://{domain of dc}/grafana/d-solo/c319354b-d0a9-4541-ae9f-d052e31fa275/sftp-right-panel-data?orgId=1&refresh=5s&from=1705574488336&to=1705596088337&panelId=1" + sftp_bottom_panel_url: "https://{domain of dc}/grafana/d-solo/c63fe588-c69c-4918-bb96-97fba722afc8/sftp-bottom-panel-data?orgId=1&refresh=5s&from=1705574366440&to=1705595966440&panelId=1" +# sftp connection configurations. +sftp: + listener: + # hostname of sftp connection of dp e.g. localhost + host: {host name} + port: {port of sftp connection of dc } + user: {username of sftp connection of dc } + password: {password of sftp connection of dc } + remote: + # path of sftp client for inbound e.g. /inbound + inbound_directory: {path mentioned in sftp client for inbound} + outbound_directory: {path mentioned in sftp client for outbound} + local: + inbound_directory: {path created in local machine for inbound} + outbound_directory: {path created in local machine for inbound} + + # add dp as per requirement and add all these fields + dp1: + # hostname of sftp connection of dp e.g. localhost + host: {host name} + port: {port of sftp connection of dp } + user: {username of sftp connection of dp } + password: {password of sftp connection of dp } + remote: + # path of sftp client for inbound e.g. /inbound + inbound_directory: {path mentioned in sftp client for inbound} +sunbird: + save: + response_data: http://{host}:{port of sunbird}/api/v1/Response_Data + response_tracker: http://{host}:{port of sunbird}/api/v1/Response_Tracker + enabled: true + elasticsearch: + host: {domain of elasticsearch like if running on local env i.e localhost} + port: {port mention in docker-compose for elastic search} + scheme: http +```` +8. Add RegistryConfig.java class in config package +```` +@Service +@Slf4j +public class RegistryConfig {} +```` +9. Add below values which mentioned in application.yml for particular dp or multiple dps +```` + + @Value("${registry.api_urls.farmer_search_api}") + private String farmerSearchURL; + + @Value("${registry.api_urls.mobile_search_api}") + private String mobileSearchURL; + + @Value("${dashboard.clear_dp1_db_endpoint_url}") + private String farmerClearDbURL; + + @Value("${dashboard.clear_dp2_db_endpoint_url}") + private String mobileClearDbURL; + + @Value("${keycloak.from_dp.farmer.clientId}") + private String farmerClientId; + + @Value("${keycloak.from_dp.farmer.clientSecret}") + private String farmerClientSecret; + + @Value("${keycloak.from_dp.farmer.url}") + private String keycloakFarmerTokenUrl; + + @Value("${keycloak.from_dp.mobile.url}") + private String keycloakMobileTokenUrl; + + @Value("${keycloak.from_dp.mobile.clientId}") + private String mobileClientId; + + @Value("${keycloak.from_dp.mobile.clientSecret}") + private String mobileClientSecret; + + @Value("${crypto.to_dp_farmer.support_encryption}") + private boolean isFarmerEncrypt; + + @Value("${crypto.to_dp_farmer.support_signature}") + private boolean isFarmerSign; + + @Value("${crypto.to_dp_mobile.support_encryption}") + private boolean isMobileEncrypt; + + @Value("${crypto.to_dp_mobile.support_signature}") + private boolean isMobileSign; + + @Value("${crypto.to_dp_mobile.key_path}") + private String mobileKeyPath; + + @Value("${crypto.to_dp_farmer.key_path}") + private String farmerKeyPath; + + @Value("${crypto.to_dp_farmer.password}") + private String farmerKeyPassword; + + @Value("${crypto.to_dp_mobile.password}") + private String mobileKeyPassword; + + @Value("${sftp.dp1.host}") + private String sftpDp1Host; + + @Value("${sftp.dp1.port}") + private int sftpDp1Port; + + @Value("${sftp.dp1.user}") + private String sftpDp1User; + + @Value("${sftp.dp1.password}") + private String sftpDp1Password; + + @Value("${sftp.dp1.remote.inbound_directory}") + private String sftpDp1RemoteInboundDirectory; + + @Value("${sftp.dp2.host}") + private String sftpDp2Host; + + @Value("${sftp.dp2.port}") + private int sftpDp2Port; + + @Value("${sftp.dp2.user}") + private String sftpDp2User; + + @Value("${sftp.dp2.password}") + private String sftpDp2Password; + + @Value("${sftp.dp2.remote.inbound_directory}") + private String sftpDp2RemoteInboundDirectory; + + @Value("${crypto.sample.password}") + private String dummyKeyPassword; + + @Value("${crypto.sample.key.path}") + private String dummyKeyPath; + + @Value("${registry.api_urls.farmer_status_api}") + private String farmerStatusUrl; + + @Value("${registry.api_urls.mobile_status_api}") + private String mobileStatusUrl; + +```` +10. Add below method getFarmerRegistryMap() of particular dp and create same methods for multiple dps. +```` + private Map getFarmerRegistryMap() { + Map farmerRegistryMap = new HashMap<>(); + farmerRegistryMap.put(CoreConstants.QUERY_NAME, "paid_farmer"); + farmerRegistryMap.put(CoreConstants.REG_TYPE, "ns:FARMER_REGISTRY"); + farmerRegistryMap.put(CoreConstants.REG_SUB_TYPE, ""); + farmerRegistryMap.put(CoreConstants.QUERY_TYPE, "namedQuery"); + farmerRegistryMap.put(CoreConstants.SORT_ATTRIBUTE, "farmer_id"); + farmerRegistryMap.put(CoreConstants.SORT_ORDER, SortOrderEnum.ASC.toValue()); + farmerRegistryMap.put(CoreConstants.PAGE_NUMBER, "1"); + farmerRegistryMap.put(CoreConstants.PAGE_SIZE, "10"); + farmerRegistryMap.put(CoreConstants.KEYCLOAK_URL, keycloakFarmerTokenUrl); + farmerRegistryMap.put(CoreConstants.KEYCLOAK_CLIENT_ID, farmerClientId); + farmerRegistryMap.put(CoreConstants.KEYCLOAK_CLIENT_SECRET, farmerClientSecret); + farmerRegistryMap.put(CoreConstants.SUPPORT_ENCRYPTION, "" + isFarmerEncrypt); + farmerRegistryMap.put(CoreConstants.SUPPORT_SIGNATURE, "" + isFarmerSign); + if(isSignEncrypt.equals("2")){ + farmerRegistryMap.put(CoreConstants.KEY_PATH, dummyKeyPath); + farmerRegistryMap.put(CoreConstants.KEY_PASSWORD, dummyKeyPassword); + } else { + farmerRegistryMap.put(CoreConstants.KEY_PATH, farmerKeyPath); + farmerRegistryMap.put(CoreConstants.KEY_PASSWORD, farmerKeyPassword); + } + farmerRegistryMap.put(CoreConstants.DP_SEARCH_URL, farmerSearchURL); + farmerRegistryMap.put(CoreConstants.DP_CLEAR_DB_URL, farmerClearDbURL); + farmerRegistryMap.put(SftpConstants.SFTP_HOST, sftpDp1Host); + farmerRegistryMap.put(SftpConstants.SFTP_PORT, String.valueOf(sftpDp1Port)); + farmerRegistryMap.put(SftpConstants.SFTP_USER, sftpDp1User); + farmerRegistryMap.put(SftpConstants.SFTP_PASSWORD, sftpDp1Password); + farmerRegistryMap.put(SftpConstants.SFTP_SESSION_CONFIG, "no"); + farmerRegistryMap.put(SftpConstants.SFTP_ALLOW_UNKNOWN_KEYS, String.valueOf(true)); + farmerRegistryMap.put(SftpConstants.SFTP_REMOTE_INBOUND_DIRECTORY, sftpDp1RemoteInboundDirectory); + farmerRegistryMap.put(CoreConstants.DP_STATUS_URL , farmerStatusUrl); + + return farmerRegistryMap; + } +```` +11. Add below 2 methods to create map of particular dp. + - In getQueryParamsConfig() create map for particular dp for specific queryparams declared in specification and add it in queryParam map. + - In getRegistrySpecificConfig() in this method particular dp's map creation method will call and map will created with particular dp key and returned. +```` + public Map getQueryParamsConfig() { + Map queryParamsConfig = new HashMap<>(); + + Map farmerRegistryMap = new HashMap<>(); + farmerRegistryMap.put("farmer_id", ""); + farmerRegistryMap.put("season", ""); + + queryParamsConfig.put(Constants.FARMER_REGISTRY, farmerRegistryMap); + return queryParamsConfig; + } + + /** + * Map to represent which common values to be used to generate request for a registry + * + * @return Map to represent registry specific config values + */ + public Map getRegistrySpecificConfig() { + Map queryParamsConfig = new HashMap<>(); + + Map farmerRegistryMap = getFarmerRegistryMap(); + + queryParamsConfig.put(Constants.FARMER_REGISTRY, farmerRegistryMap); + return queryParamsConfig; + } +```` + +12. Create DcController in controller.rest package +```` +@RestController +@Slf4j +@RequestMapping(produces = "application/json") +@Tag(name = "Data Consumer", description = "DC APIs") +public class DcController {} +```` +13. Create below entrypoint for triggering dc communication using only one data. +```` + @Operation(summary = "Receive consumer search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/public/api/v1/consumer/search/payload") + public AcknowledgementDTO createSearchRequestsFromPayload(@RequestBody Map payloadMap) throws Exception { + log.info("Payload received from postman"); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + if (ObjectUtils.isNotEmpty(payloadMap)) { + acknowledgementDTO = dcRequestBuilderService.generateRequest(Collections.singletonList(payloadMap), + CoreConstants.SEND_PROTOCOL_HTTPS, "", "",""); + } + return acknowledgementDTO; + } +```` +14. To run above entrypoint refer below curl. +```` +curl --location 'http://localhost:8000/public/api/v1/consumer/search/payload' \ +--header 'Content-Type: application/json' \ +--data '{ + "farmer_id" : "F-1", + "farmer_name" : "Farmer-1", + "mobile_number" : "9767670153", + "season" : "2023-xyz" + +}' + +```` +15. Create below entry point for triggering dc communication for multiple data using csv file. +```` + @Operation(summary = "Receive consumer search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/consumer/search/csv") + public AcknowledgementDTO createSearchRequestsFromCsv(@RequestPart(value = "file") MultipartFile payloadFile) throws Exception { + log.info("Payload received from csv file"); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + if (ObjectUtils.isNotEmpty(payloadFile)) { + Path tempFile = Paths.get(System.getProperty("java.io.tmpdir"), "payload.csv"); + if (Files.exists(tempFile)) { + commonUtils.deleteFolder(tempFile); + } + Files.createFile(tempFile); + payloadFile.transferTo(tempFile.toFile()); + acknowledgementDTO = dcRequestBuilderService.generateRequest( + requestBuilderService.generatePayloadFromCsv(tempFile.toFile()), CoreConstants.SEND_PROTOCOL_HTTPS, + dcRequestBuilderService.demoTestEncryptionSignature(tempFile.toFile()), + payloadFile.getName(), ""); + Files.delete(tempFile); + } + return acknowledgementDTO; + } +```` +16. To run above entrypoint refer below curl and create one payload.csv with multiple data. +```` +curl --location 'localhost:8000/private/api/v1/consumer/search/csv' \ +--form 'file=@"/home/ttpl-rt-119/Downloads/payload.csv"' +```` +17. Create below endpoint for clearing db in dc. +```` + @GetMapping("/public/api/v1/registry/clear-db") + public void clearDb() throws G2pHttpException, IOException { + commonUtils.handleToken(); + responseDataRepository.deleteAll(); + log.info("DC DB cleared"); + + for (Map.Entry configEntryMap : registryConfig.getRegistrySpecificConfig("").entrySet()) { + try { + Map registrySpecificConfigMap = (Map) registryConfig.getRegistrySpecificConfig("").get(configEntryMap.getKey()); + String jwtToken = requestBuilderService.getValidatedToken(registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_URL).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_ID).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_SECRET).toString()); + log.info("jwtToken: {}", jwtToken); + log.info("url: {}", registrySpecificConfigMap.get(CoreConstants.DP_CLEAR_DB_URL).toString()); + HttpResponse response = g2pUnirestHelper.g2pGet(registrySpecificConfigMap.get(CoreConstants.DP_CLEAR_DB_URL).toString()) + .header("Content-Type", "application/json") + .header("Authorization", jwtToken) + .asString(); + log.info("DP " + registrySpecificConfigMap.get(CoreConstants.REG_TYPE) + " DB cleared with response " + response.getStatus()); + } catch (Exception e) { + log.error("Exception in clearDb: ", e); + } + } + Set keys = redisTemplate.keys("*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + log.info("DC Redis cache cleared"); + } +```` +18. Add below exception handling in the DC controller. +```` +@ExceptionHandler(value = G2pcValidationException.class) +@ResponseStatus(HttpStatus.BAD_REQUEST) +public ValidationErrorResponse handleValidationException(G2pcValidationException ex) { + return new ValidationErrorResponse(ex.getG2PcErrorList()); +} + +@ExceptionHandler(value = G2pHttpException.class) +@ResponseStatus(HttpStatus.UNAUTHORIZED) +public ErrorResponse handleG2pHttpStatusException(G2pHttpException ex) { + return new ErrorResponse(ex.getG2PcError()); +} +```` +19. Create RegResponseDTO for particular data provider in dto.(dp-name) package. Shown below +```` +package g2pc.ref.dc.client.dto.farmer.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class RegRecordFarmerDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("farmer_name") + private String farmerName; + + @JsonProperty("season") + private String season; + + @JsonProperty("payment_status") + private String paymentStatus; + + @JsonProperty("payment_date") + private String paymentDate; + + @JsonProperty("payment_amount") + private Double paymentAmount; +} +```` +20. Declare below interface DcRequestBuilderService to generateRequest for single data and multiple data in csv file. +```` +public interface DcRequestBuilderService { + + AcknowledgementDTO generateRequest(List> payloadMapList, String protocol, + String isSignEncrypt, String payloadFilename, String inboundFilename) throws Exception; + + AcknowledgementDTO generateStatusRequest(String transactionID, String transactionType, String protocol) throws Exception; +} +```` +21. Create DcRequestBuilderServiceImpl.java class and implement it from DcRequestBuilderService interface. +```` +@Service +@Slf4j +public class DcRequestBuilderServiceImpl implements DcRequestBuilderService { +```` +22. Add below autowired beans in above class. +```` + @Autowired + private RequestBuilderService requestBuilderService; + + @Autowired + RegistryConfig registryConfig; + + @Autowired + TxnTrackerService txnTrackerService; + + @Autowired + private ResourceLoader resourceLoader; + + @Autowired + ResponseTrackerRepository responseTrackerRepository; + + @Autowired + private ElasticsearchService elasticsearchService; +```` +23. Override below method generateRequest() from interface. + 1. Fetch queryMapList using payload list provided and registryconfig. + 2. In for loop iterate configEntryMap fetching from registryConfig. + 3. Filter out queryMapFilteredList using registryType. + 4. Build searchCriteriaDto by calling dc-core method by passing queryParams and registrySpecificationConfig. + 5. Generate transaction id. + 6. Build searchString using method buildRequest from dc-core. + 7. Check if request is http or sftp. + 8. In http call sendRequest method by passing all arguments. + 9. Put returned g2pcError in g2pcErrorMap. + 10. If request is sftp , create SftpServerConfigDTO. + 11. Invoke sendRequestSftp method and put returned g2pcError in g2pcErrorMap. + 12. Save initial transaction and requestString in redis. + 13. Save data in db. +```` + public AcknowledgementDTO generateRequest(List> payloadMapList, String protocol, + String isSignEncrypt, String payloadFilename, String inboundFilename) throws Exception { + Map g2pcErrorMap = new HashMap<>(); + List> queryMapList = requestBuilderService.createQueryMap(payloadMapList, registryConfig.getQueryParamsConfig().entrySet()); + for (Map.Entry configEntryMap : registryConfig.getRegistrySpecificConfig(isSignEncrypt).entrySet()) { + List> queryMapFilteredList = queryMapList.stream() + .map(map -> map.entrySet().stream() + .filter(entry -> entry.getKey().equals(configEntryMap.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))).toList(); + Map registrySpecificConfigMap = (Map) registryConfig.getRegistrySpecificConfig(isSignEncrypt).get(configEntryMap.getKey()); + List searchCriteriaDTOList = new ArrayList<>(); + for (Map queryParamsMap : queryMapFilteredList) { + SearchCriteriaDTO searchCriteriaDTO = requestBuilderService.getSearchCriteriaDTO(queryParamsMap, registrySpecificConfigMap); + searchCriteriaDTOList.add(searchCriteriaDTO); + } + String transactionId = CommonUtils.generateUniqueId("T"); + String requestString = requestBuilderService.buildRequest(searchCriteriaDTOList, transactionId, ActionsENUM.SEARCH); + String encryptedSalt = ""; + G2pcError g2pcError = new G2pcError(); + switch (isSignEncrypt) { + case "0": + break; + case "1": + encryptedSalt = "salt"; + case "2": + break; + } + try { + if (protocol.equals(CoreConstants.SEND_PROTOCOL_HTTPS)) { + Resource resource = resourceLoader.getResource(registrySpecificConfigMap.get(CoreConstants.KEY_PATH).toString()); + + InputStream fis = resource.getInputStream(); + g2pcError = requestBuilderService.sendRequest(requestString, + registrySpecificConfigMap.get(CoreConstants.DP_SEARCH_URL).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_ID).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_SECRET).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_URL).toString(), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_ENCRYPTION).toString()), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_SIGNATURE).toString()), + fis, encryptedSalt, + registrySpecificConfigMap.get(CoreConstants.KEY_PASSWORD).toString(), CoreConstants.SEARCH_TXN_TYPE); + g2pcErrorMap.put(configEntryMap.getKey(), g2pcError); + log.info("DP_SEARCH_URL = {}", registrySpecificConfigMap.get(CoreConstants.DP_SEARCH_URL).toString()); + } else if (protocol.equals(CoreConstants.SEND_PROTOCOL_SFTP)) { + SftpServerConfigDTO sftpServerConfigDTO = new SftpServerConfigDTO(); + sftpServerConfigDTO.setUser(registrySpecificConfigMap.get(SftpConstants.SFTP_USER).toString()); + sftpServerConfigDTO.setHost(registrySpecificConfigMap.get(SftpConstants.SFTP_HOST).toString()); + sftpServerConfigDTO.setPort(Integer.parseInt(registrySpecificConfigMap.get(SftpConstants.SFTP_PORT).toString())); + sftpServerConfigDTO.setPassword(registrySpecificConfigMap.get(SftpConstants.SFTP_PASSWORD).toString()); + sftpServerConfigDTO.setStrictHostKeyChecking(registrySpecificConfigMap.get(SftpConstants.SFTP_SESSION_CONFIG).toString()); + sftpServerConfigDTO.setRemoteInboundDirectory(registrySpecificConfigMap.get(SftpConstants.SFTP_REMOTE_INBOUND_DIRECTORY).toString()); + + Resource resource = resourceLoader.getResource(registrySpecificConfigMap.get(CoreConstants.KEY_PATH).toString()); + InputStream fis = resource.getInputStream(); + inboundFilename = UUID.randomUUID() + ".json"; + g2pcError = requestBuilderService.sendRequestSftp(requestString, + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_ENCRYPTION).toString()), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_SIGNATURE).toString()), + fis, encryptedSalt, + registrySpecificConfigMap.get(CoreConstants.KEY_PASSWORD).toString(), CoreConstants.SEARCH_TXN_TYPE, + sftpServerConfigDTO, inboundFilename); + g2pcErrorMap.put(configEntryMap.getKey(), g2pcError); + if (g2pcError != null && g2pcError.getCode().contains("err")) { + log.info("Uploaded failed for : {}", registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + throw new Exception("Uploaded failed for : " + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + } else { + log.info("Uploaded to inbound of : {}", registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + } + } + txnTrackerService.saveInitialTransaction(payloadMapList, transactionId, HeaderStatusENUM.RCVD.toValue(), protocol); + txnTrackerService.saveRequestTransaction(requestString, + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString(), transactionId, protocol); + txnTrackerService.saveRequestInDB(requestString, + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString(), protocol, g2pcError, + payloadFilename, inboundFilename); + } catch (Exception e) { + log.error(Constants.GENERATE_REQUEST_ERROR_MESSAGE + ": {}", e.getMessage()); + } + } + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + acknowledgementDTO.setMessage(g2pcErrorMap); + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + return acknowledgementDTO; + } +```` +24. Create DcSftpListener.java to handle inbound and outbound messages in package g2pc.ref.dc.client.controller.sftp + 1. Values added in this class are inbound and outbound file path mentioned in application.yml. + 2. handleMessageInbound() method written to handle inbound message. +```` +package g2pc.ref.farmer.regsvc.controller.sftp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.ref.farmer.regsvc.constants.Constants; +import g2pc.ref.farmer.regsvc.dto.SftpDpData; +import g2pc.ref.farmer.regsvc.service.DpSftpPushUpdateService; +import g2pc.ref.farmer.regsvc.service.FarmerValidationService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.web.server.ResponseStatusException; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +@Configuration +@Slf4j +public class DcSftpListener { + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpLocalDirectoryInbound; + + + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + FarmerValidationService farmerValidationService; + + @SuppressWarnings("unchecked") + @ServiceActivator(inputChannel = "sftpInbound") + public void handleMessageInbound(Message message) { + try { + File file = message.getPayload(); + log.info("Received Message from inbound directory of dp-1: {}", file.getName()); + if (ObjectUtils.isNotEmpty(file) && file.getName().contains(".json")) { + String requestString = new String(Files.readAllBytes(file.toPath())); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); + RequestMessageDTO messageDTO; + + Map metaData = (Map) requestDTO.getHeader().getMeta().getData(); + + messageDTO = farmerValidationService.signatureValidation(metaData, requestDTO); + requestDTO.setMessage(messageDTO); + String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); + try { + farmerValidationService.validateRequestDTO(requestDTO); + requestHandlerService.buildCacheRequest( + objectMapper.writeValueAsString(requestDTO), cacheKey, CoreConstants.SEND_PROTOCOL_SFTP); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } + Files.deleteIfExists(Path.of(sftpLocalDirectoryInbound + "/" + file.getName())); + } catch (Exception e) { + log.error("Error: ", e); + } + } + + @ServiceActivator(inputChannel = "errorChannel") + public void handleError(Message message) { + Throwable error = (Throwable) message.getPayload(); + log.error("Handling ERROR: {}", error.getMessage()); + } +} + +```` +25. Create on-search endpoint , refer below snippet ,there are methods called in this methods refer code after this point. +```` + @Operation(summary = "Listen to registry response") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.ON_SEARCH_RESPONSE_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/registry/on-search") + public AcknowledgementDTO handleOnSearchResponse(@RequestBody String responseString) throws Exception { + commonUtils.handleToken(); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + ResponseDTO responseDTO = objectMapper.readerFor(ResponseDTO.class). + readValue(responseString); + ResponseMessageDTO messageDTO; + Map metaData = (Map) responseDTO.getHeader().getMeta().getData(); + messageDTO = dcValidationService.signatureValidation(metaData, responseDTO); + responseDTO.setMessage(messageDTO); + try { + dcValidationService.validateResponseDto(responseDTO); + if (ObjectUtils.isNotEmpty(responseDTO)) { + acknowledgementDTO = dcResponseHandlerService.getResponse(responseDTO); + } + } catch (JsonProcessingException | IllegalArgumentException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } + return acknowledgementDTO; + } +```` +26. Create DcCommonUtil.java in util package and create handle token method to handle and validate token. +```` +package g2pc.ref.dc.client.utils; +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.security.BearerTokenUtil; +import g2pc.core.lib.security.service.G2pTokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Service +public class DcCommonUtils { +} + +```` +27. Add below values from application.yml to validate token +```` + @Autowired + G2pTokenService g2pTokenService; + + @Value("${keycloak.dc.client.realm}") + private String keycloakRealm; + + @Value("${keycloak.dc.client.url}") + private String keycloakURL; + + @Value("${keycloak.dc.master.url}") + private String masterUrl; + + @Value("${keycloak.dc.master.getClientUrl}") + private String getClientUrl; + + @Value("${keycloak.dc.client.clientId}") + private String dcClientId; + + @Value("${keycloak.dc.client.clientSecret}") + private String dcClientSecret; + + @Value("${keycloak.dc.master.clientId}") + private String masterClientId; + + @Value("${keycloak.dc.master.clientSecret}") + private String masterClientSecret; + + @Value("${keycloak.dc.username}") + private String adminUsername; + + @Value("${keycloak.dc.password}") + private String adminPassword; + + @Value("${sftp.listener.host}") + private String sftpDcHost; + + @Value("${sftp.listener.port}") + private int sftpDcPort; + + @Value("${sftp.listener.user}") + private String sftpDcUser; + + @Value("${sftp.listener.password}") + private String sftpDcPassword; + + @Value("${sftp.listener.remote.inbound_directory}") + private String sftpDcRemoteInboundDirectory; + + @Value("${sftp.listener.remote.outbound_directory}") + private String sftpDcRemoteOutboundDirectory; + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpDcLocalInboundDirectory; + + @Value("${sftp.listener.local.outbound_directory}") + private String sftpDcLocalOutboundDirectory; + +```` +28. Add below methods in DcCommonUtils.java to handle and validate token and to get sftp configuration for dc. +```` + public void handleToken() throws G2pHttpException, JsonProcessingException { + String token = BearerTokenUtil.getBearerTokenHeader(); + String introspect = keycloakURL + "/introspect"; + ResponseEntity introspectResponse = g2pTokenService.getInterSpectResponse(introspect, token, dcClientId, dcClientSecret); + if (introspectResponse.getStatusCode().value() == 401) { + throw new G2pHttpException(new G2pcError(introspectResponse.getStatusCode().toString(), introspectResponse.getBody())); + } + if (!g2pTokenService.validateToken(masterUrl, getClientUrl, g2pTokenService.decodeToken(token), + masterClientId, masterClientSecret, adminUsername, adminPassword)) { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_USER_UNAUTHORIZED.toValue(), "User is not authorized")); + } + } + + public SftpServerConfigDTO getSftpConfigForDc() { + SftpServerConfigDTO sftpServerConfigDTO = new SftpServerConfigDTO(); + sftpServerConfigDTO.setHost(sftpDcHost); + sftpServerConfigDTO.setPort(sftpDcPort); + sftpServerConfigDTO.setUser(sftpDcUser); + sftpServerConfigDTO.setPassword(sftpDcPassword); + sftpServerConfigDTO.setAllowUnknownKeys(true); + sftpServerConfigDTO.setStrictHostKeyChecking("no"); + sftpServerConfigDTO.setRemoteInboundDirectory(sftpDcRemoteInboundDirectory); + sftpServerConfigDTO.setRemoteOutboundDirectory(sftpDcRemoteOutboundDirectory); + sftpServerConfigDTO.setLocalInboundDirectory(sftpDcLocalInboundDirectory); + sftpServerConfigDTO.setLocalOutboundDirectory(sftpDcLocalOutboundDirectory); + return sftpServerConfigDTO; + } + public void deleteFolder(Path path) { + try { + if (Files.isRegularFile(path)) { + Files.delete(path); + return; + } + try (Stream paths = Files.walk(path)) { + paths.filter(p -> p.compareTo(path) != 0).forEach(p -> deleteFolder(p)); // delete all the children folders or files; + Files.delete(path); // delete the folder itself; + } + } catch (IOException ignored) { + ignored.printStackTrace(); + } + } +```` +29. Create DcValidationService interface with 3 methods , validateRegRecords() this method is dc specific , to validate query params. +```` +@Service +public interface DcValidationService { + + public void validateResponseDto(ResponseDTO responseDTO) throws Exception; + + ResponseMessageDTO signatureValidation(Map metaData, ResponseDTO responseDTO) throws Exception; + + StatusResponseMessageDTO signatureValidation(Map metaData, StatusResponseDTO statusResponseDTO) throws Exception; + + void validateStatusResponseDTO(StatusResponseDTO statusResponseDTO) throws IOException, G2pcValidationException; +} +```` +30. Create DcValidationServiceImpl class to implement DcValidationService interface and override method. +```` +@Service +@Slf4j +public class DcValidationServiceImpl implements DcValidationService { +```` +31. Add below autowired beans in DcValidationServiceImpl. Add below values for all required dps. +```` + @Autowired + ResponseHandlerService responseHandlerService; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + + @Autowired + private AsymmetricSignatureService asymmetricSignatureService; + + @Autowired + private ResourceLoader resourceLoader; + + @Value("${crypto.from_dp_farmer.support_encryption}") + private boolean isFarmerEncrypt; + + @Value("${crypto.from_dp_farmer.support_signature}") + private boolean isFarmerSign; + + @Value("${crypto.from_dp_farmer.password}") + private String farmerp12Password; + + @Value("${crypto.from_dp_farmer.key_path}") + private String farmerKeyPath; + + @Value("${crypto.from_dp_farmer.id}") + private String farmerID; +```` +32. Override below signatureValidation() method and add required if conditions for required dps. + 1. Check get string from CoreConstants.DP_ID for respective farmer ids , and add respective attributes. + 2. Check isSign flag , if yes check metadata given from controller is true or false , if not true throw error that configurations are not valid. + 2. Take farmerKeyPath for dp .p12 file. + 3. Check isEncrypt flag , if true check isMsgEncrypt flag from header is true or false , if not true throw error that configurations are not valid. + 4. Recreate signature using header and message. Verify both signature if error from verifySignature throw error signature invalid. + 5. Decrypt message , if successfully decrypted add in requestDto. + 6. Above logic is same in 4 cases of signature and encryption mentioned "Overview / List of libraries". + +```` + @Override + public ResponseMessageDTO signatureValidation(Map metaData, ResponseDTO responseDTO) throws Exception { + + + String p12Password =""; + boolean isEncrypt = false; + boolean isSign=false; + String keyPath=""; + if(metaData.get(CoreConstants.DP_ID).equals(farmerID)){ + p12Password = farmerp12Password; + isEncrypt = isFarmerEncrypt; + isSign = isFarmerSign; + keyPath = farmerKeyPath; + } else if(metaData.get(CoreConstants.DP_ID).equals(mobileID)){ + p12Password = mobilep12Password; + isEncrypt=isMobileEncrypt; + isSign = isMobileSign; + keyPath = mobileKeyPath; + } + log.info("Is encrypted ? -> "+isEncrypt); + log.info("Is signed ? -> "+isSign); + ObjectMapper objectMapper = new ObjectMapper(); + ResponseMessageDTO messageDTO; + + + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(keyPath); + InputStream fis = resource.getInputStream(); + + if(isEncrypt){ + if(!responseDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String responseHeaderString = objectMapper.writeValueAsString(responseDTO.getHeader()); + String responseSignature = responseDTO.getSignature(); + String messageString = responseDTO.getMessage().toString(); + String data = responseHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(responseDTO.getHeader().getIsMsgEncrypted()){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(ResponseMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(responseDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(responseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, ResponseMessageDTO.class); + String responseHeaderString = objectMapper.writeValueAsString(responseDTO.getHeader()); + String responseSignature = responseDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = responseHeaderString+messageString; + log.info("Signature ->"+responseSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!responseDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = responseDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(),"Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(ResponseMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(responseDTO.getHeader().getIsMsgEncrypted()){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(responseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, ResponseMessageDTO.class); + } + } + return messageDTO; + } +```` +33. Override validateResponse() to validate ResponseDTO +```` + @Override + public void validateResponseDto(ResponseDTO responseDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(responseDTO.getHeader()); + ResponseHeaderDTO headerDTO = objectMapper.readerFor(ResponseHeaderDTO.class). + readValue(headerString); + responseHandlerService.validateResponseHeader(headerDTO); + byte[] json = objectMapper.writeValueAsBytes(responseDTO.getMessage()); + ResponseMessageDTO messageDTO = objectMapper.readValue(json, ResponseMessageDTO.class); + responseHandlerService.validateResponseMessage(messageDTO); + } +```` +34. Create DcResponseHandlerService interface to handle the response +```` +public interface DcResponseHandlerService { + + AcknowledgementDTO getResponse(ResponseDTO responseDTO) throws JsonProcessingException; +} +```` +35. Create DcResponseHandlerServiceImpl class which implements DcResponseHandlerService interface +```` +@Service +@Slf4j +public class DcResponseHandlerServiceImpl implements DcResponseHandlerService { + + @Autowired + private TxnTrackerService txnTrackerService; + + @Override + public AcknowledgementDTO getResponse(ResponseDTO responseDTO) throws JsonProcessingException { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + + G2pcError g2pcError = txnTrackerService.updateTransactionDbAndCache(responseDTO, outboundFilename); + log.info("on-search response received from registry : {}", objectMapper.writeValueAsString(responseDTO)); + log.info("on-search database updation response from sunbird - "+g2pcError.getCode()); + if (g2pcError.getCode().equals(HttpStatus.OK.toString())){ + acknowledgementDTO.setMessage(Constants.ON_SEARCH_RESPONSE_RECEIVED.toString()); + acknowledgementDTO.setStatus(Constants.COMPLETED); + + } else { + acknowledgementDTO.setMessage(Constants.INVALID_RESPONSE.toString()); + acknowledgementDTO.setStatus(Constants.PENDING); + throw new G2pHttpException(g2pcError); + + } + + return acknowledgementDTO; + } +} +```` +36. Create new entrypoint method for /status. +```` + @Operation(summary = "Receive consumer search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/consumer/status/payload") + public AcknowledgementDTO createStatusRequest(@RequestParam String transactionId , @RequestParam String transactionType) throws Exception { + log.info("Payload received from csv file"); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + if (ObjectUtils.isNotEmpty(transactionId)) { + acknowledgementDTO = dcRequestBuilderService.generateStatusRequest(transactionId,transactionType); + } + return acknowledgementDTO; + } +```` +37. Refer below curl to execute above entrypoint. In which you can see we are sending transactionId for which we wanted to know status and action/transactionType for which we are searching status. +```` +curl --location \ + --request \ + POST 'localhost:8000/private/api/v1/consumer/status/payload?transactionId=T757-5372-9253-9725-4673&transactionType=search' +```` +38. Declare below method in DcRequestBuilderService interface to generate request for /status endpoint. +```` + AcknowledgementDTO generateStatusRequest(String transactionID,String transactionType) throws Exception; +```` +39. Override the above method in DcRequestBuilderServiceImpl class. + 1. Generate unique transaction id for status transaction. + 2. Find registryType from response_tracker and response using transactionId given from postman. + 3. Fetch registry specific map from registryConfig class , as it returns values specified for registry like url and credentials. + 4. Call buildTransactionRequest() method by passing transaction id and transactionType given by postman. + 5. Invoke buildStatusRequest() method by passing txnStatusRequestDTO,statusRequestTransactionId and type of transaction. + 6. Create resource and inputstream using keypath mentioned in application.yml. + 7. Call sendRequest() and pass dp status endpoint url, keycloak client id , client secret , url , encryption-signature flags , .p12 file password and status url flag to identify the method is called for status operation. + 8. Invoke saveInitialStatusTransaction() , to save initial transaction id dc redis. + 9. Call saveRequestTransaction() , to save requestString along transactionId. + 10. Call saveRequestInStatusDB() , to save the data in dc side tables. +```` + @Override + public AcknowledgementDTO generateStatusRequest(String transactionID,String transactionType) throws Exception { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + String statusRequestTransactionId = CommonUtils.generateUniqueId("T"); + ObjectMapper objectMapper = new ObjectMapper(); + String encryptedSalt = ""; + Map fieldValues = new HashMap<>(); + fieldValues.put("transaction_id.keyword",transactionID); + SearchResponse responseTrackerSearchResponse = elasticsearchService.exactSearch("response_tracker", fieldValues); + if (responseTrackerSearchResponse.getHits().getHits().length > 0) { + + String responseTrackerDtoString = responseTrackerSearchResponse.getHits().getHits()[0].getSourceAsString(); + ResponseTrackerDto responseTrackerDto = objectMapper.readerFor(ResponseTrackerDto.class). + readValue(responseTrackerDtoString); + String registryType = responseTrackerDto.getRegistryType().substring(3).toLowerCase(); + Map registrySpecificConfigMap = (Map) registryConfig.getRegistrySpecificConfig("").get(registryType); + TxnStatusRequestDTO txnStatusRequestDTO = requestBuilderService.buildTransactionRequest(transactionID, transactionType); + String statusRequestString = requestBuilderService.buildStatusRequest(txnStatusRequestDTO, statusRequestTransactionId, ActionsENUM.STATUS); + G2pcError g2pcError = null; + try { + Resource resource = resourceLoader.getResource(registrySpecificConfigMap.get(CoreConstants.KEY_PATH).toString()); + InputStream fis = resource.getInputStream(); + g2pcError = requestBuilderService.sendRequest(statusRequestString, + registrySpecificConfigMap.get(CoreConstants.DP_STATUS_URL).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_ID).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_SECRET).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_URL).toString(), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_ENCRYPTION).toString()), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_SIGNATURE).toString()), + fis, encryptedSalt, + registrySpecificConfigMap.get(CoreConstants.KEY_PASSWORD).toString(), "status"); + log.info("" + g2pcError); + } catch (Exception e) { + log.error(Constants.GENERATE_REQUEST_ERROR_MESSAGE, e); + } + txnTrackerService.saveInitialStatusTransaction(transactionType, statusRequestTransactionId, HeaderStatusENUM.RCVD.toValue(), protocol); + txnTrackerService.saveRequestTransaction(statusRequestString, + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString(), statusRequestTransactionId, protocol); + txnTrackerService.saveRequestInStatusDB(statusRequestString, registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + acknowledgementDTO.setMessage(g2pcError); + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + } else { + G2pcError g2pcError = new G2pcError(); + g2pcError.setCode(ExceptionsENUM.ERROR_REQUEST_NOT_FOUND.toValue()); + g2pcError.setMessage("Data for transaction id " + transactionID + "is not found"); + acknowledgementDTO.setMessage(g2pcError); + } + return acknowledgementDTO; + } +```` +40. Define the below method in DcController to create /on-status endpoint which will get call from dp side. + 1. call commonUtils.handleToken() method to authenticate user. + 2. Declare objectMapper and add neccessary dependencies. + 3. Get statusResponseDto by converting string to object using objectMapper. + 4. Get validated signature statusResponseMessageDto by call signatureValidation() method. + 5. Validated statusResponseDto as per g2p specification. + 6. call getStatusResponse(). +```` + @SuppressWarnings("unchecked") + @Operation(summary = "Listen to registry response") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.ON_SEARCH_RESPONSE_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/registry/on-status") + public AcknowledgementDTO handleOnStatusResponse(@RequestBody String responseString) throws Exception { + commonUtils.handleToken(); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + StatusResponseDTO statusResponseDTO = objectMapper.readerFor(StatusResponseDTO.class). + readValue(responseString); + StatusResponseMessageDTO statusResponseMessageDTO; + Map metaData = (Map) statusResponseDTO.getHeader().getMeta().getData(); + statusResponseMessageDTO = dcValidationService.signatureValidation(metaData, statusResponseDTO); + statusResponseDTO.setMessage(statusResponseMessageDTO); + try { + dcValidationService.validateStatusResponseDTO(statusResponseDTO); + if (ObjectUtils.isNotEmpty(statusResponseDTO)) { + acknowledgementDTO = dcResponseHandlerService.getStatusResponse(statusResponseDTO); + } + } catch (JsonProcessingException | IllegalArgumentException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } + return acknowledgementDTO; + } +```` +41. Add below 2 methods in DcValidationService for signatureValidation of statusResponseMessageDto and validate statusResponseDto. +```` + StatusResponseMessageDTO signatureValidation(Map metaData, StatusResponseDTO statusResponseDTO) throws Exception; + + void validateStatusResponseDTO(StatusResponseDTO statusResponseDTO) throws IOException, G2pcValidationException; +```` +42. Override signatureValidation() method in DcValidationServiveImpl. +```` +@Override + public StatusResponseMessageDTO signatureValidation(Map metaData, StatusResponseDTO statusResponseDTO) throws Exception { + String p12Password =""; + boolean isEncrypt = false; + boolean isSign= false; + String keyPath=""; + if(metaData.get(CoreConstants.DP_ID).equals(farmerID)){ + p12Password = farmerp12Password; + isEncrypt = isFarmerEncrypt; + isSign = isFarmerSign; + keyPath = farmerKeyPath; + } else if(metaData.get(CoreConstants.DP_ID).equals(mobileID)){ + p12Password = mobilep12Password; + isEncrypt=isMobileEncrypt; + isSign = isMobileSign; + keyPath = mobileKeyPath; + } + log.info("Is encrypted ? -> "+isEncrypt); + log.info("Is signed ? -> "+isSign); + ObjectMapper objectMapper = new ObjectMapper(); + StatusResponseMessageDTO messageDTO; + + + Boolean isMsgEncrypted = statusResponseDTO.getHeader().getIsMsgEncrypted(); + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(keyPath); + InputStream fis = resource.getInputStream(); + + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String responseHeaderString = objectMapper.writeValueAsString(statusResponseDTO.getHeader()); + String responseSignature = statusResponseDTO.getSignature(); + String messageString = statusResponseDTO.getMessage().toString(); + String data = responseHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(isMsgEncrypted){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusResponseMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(statusResponseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusResponseMessageDTO.class); + String responseHeaderString = objectMapper.writeValueAsString(statusResponseDTO.getHeader()); + String responseSignature = statusResponseDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = responseHeaderString+messageString; + log.info("Signature ->"+responseSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = statusResponseDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(),"Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusResponseMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(statusResponseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusResponseMessageDTO.class); + } + } + return messageDTO; + } +```` +43. Override validateStatusResponseDTO() method in DcValidationServiceImpl. +```` +@Override + public void validateStatusResponseDTO(StatusResponseDTO statusResponseDTO) throws IOException, G2pcValidationException { + ObjectMapper objectMapper = new ObjectMapper(); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(statusResponseDTO.getHeader()); + ResponseHeaderDTO headerDTO = objectMapper.readerFor(ResponseHeaderDTO.class). + readValue(headerString); + byte[] json = objectMapper.writeValueAsBytes(statusResponseDTO.getMessage()); + StatusResponseMessageDTO statusResponseMessageDTO = objectMapper.readValue(json, StatusResponseMessageDTO.class); + responseHandlerService.validateStatusResponseMessage(statusResponseMessageDTO); + } +```` +44. Add below method in DcResponseHandlerService interface. +```` +AcknowledgementDTO getStatusResponse(StatusResponseDTO statusResponseDTO) throws JsonProcessingException; + +```` +45. Override above method in DcResponseHandlerServiceImpl class. +```` +@Override + public AcknowledgementDTO getStatusResponse(StatusResponseDTO statusResponseDTO) throws JsonProcessingException { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + + G2pcError g2pcError = txnTrackerService.updateStatusTransactionDbAndCache(statusResponseDTO); + log.info("on-status response received from registry : {}", objectMapper.writeValueAsString(statusResponseDTO)); + log.info("on-status database updation response from sunbird - "+g2pcError.getCode()); + if (g2pcError.getCode().equals(HttpStatus.OK.toString())){ + acknowledgementDTO.setMessage(Constants.ON_STATUS_RESPONSE_RECEIVED.toString()); + acknowledgementDTO.setStatus(Constants.COMPLETED); + + } else { + acknowledgementDTO.setMessage(Constants.INVALID_RESPONSE.toString()); + acknowledgementDTO.setStatus(Constants.PENDING); + throw new G2pHttpException(g2pcError); + + } + return acknowledgementDTO; + } +```` +46. Create DcDashboardController.java for creating endpoints for dashboard of Grafana. +```` +@Controller +@Slf4j +public class DcDashboardController { +} +```` +47. Add below configuration values which added in application.yml +```` + @Value("${dashboard.left_panel_url}") + private String leftPanelUrl; + + @Value("${dashboard.right_panel_url}") + private String rightPanelUrl; + + @Value("${dashboard.bottom_panel_url}") + private String bottomPanelUrl; + + @Value("${dashboard.post_https_endpoint_url}") + private String postHttpsEndpointUrl; + + @Value("${dashboard.clear_dc_db_endpoint_url}") + private String clearDcDbEndpointUrl; + + @Value("${keycloak.dc.client.url}") + private String dcKeyCloakUrl; + + @Value("${keycloak.dc.client.clientId}") + private String dcClientId; + + @Value("${keycloak.dc.client.clientSecret}") + private String dcClientSecret; + + @Value("${dashboard.left_panel_data_endpoint_url}") + private String leftPanelDataEndpointUrl; + + @Value("${dashboard.sftp_post_endpoint_url}") + private String sftpPostEndpointUrl; + + @Value("${dashboard.sftp_dc_data_endpoint_url}") + private String sftpDcDataEndpointUrl; + + @Value("${dashboard.sftp_dp1_data_endpoint_url}") + private String sftpDp1DataEndpointUrl; + + @Value("${dashboard.sftp_dp2_data_endpoint_url}") + private String sftpDp2DataEndpointUrl; + + @Value("${dashboard.dc_status_endpoint_url}") + private String dcStatusEndpointUrl; + + @Value("${dashboard.sftp_left_panel_url}") + private String sftpLeftPanelUrl; + + @Value("${dashboard.sftp_right_panel_url}") + private String sftpRightPanelUrl; + + @Value("${dashboard.sftp_bottom_panel_url}") + private String sftpBottomPanelUrl; + + @Autowired + private RequestBuilderService requestBuilderService; +```` +48. Create below endpoint method +```` +@GetMapping("/dashboard") + public String showDashboardPage(Model model) throws IOException, ParseException { + String jwtToken = requestBuilderService.getValidatedToken(dcKeyCloakUrl, dcClientId, dcClientSecret); + model.addAttribute("left_panel_url", leftPanelUrl); + model.addAttribute("right_panel_url", rightPanelUrl); + model.addAttribute("bottom_panel_url", bottomPanelUrl); + model.addAttribute("post_https_endpoint_url", postHttpsEndpointUrl); + model.addAttribute("clear_dc_db_endpoint_url", clearDcDbEndpointUrl); + model.addAttribute("jwtToken", jwtToken); + model.addAttribute("left_panel_data_endpoint_url", leftPanelDataEndpointUrl); + model.addAttribute("dc_status_endpoint_url", dcStatusEndpointUrl); + return "dashboardHttps"; + } + @GetMapping("/dashboard/sftp") + public String showDashboardSftpPage(Model model) throws IOException, ParseException { + String jwtToken = requestBuilderService.getValidatedToken(dcKeyCloakUrl, dcClientId, dcClientSecret); + model.addAttribute("sftp_post_endpoint_url", sftpPostEndpointUrl); + model.addAttribute("sftp_left_panel_url", sftpLeftPanelUrl); + model.addAttribute("sftp_right_panel_url", sftpRightPanelUrl); + model.addAttribute("sftp_bottom_panel_url", sftpBottomPanelUrl); + model.addAttribute("clear_dc_db_endpoint_url", clearDcDbEndpointUrl); + model.addAttribute("jwtToken", jwtToken); + return "dashboardSftp"; + } + + @GetMapping("/dashboard/sftp/sse") + public String showDashboardSftpPageSse(Model model) throws IOException, ParseException { + String jwtToken = requestBuilderService.getValidatedToken(dcKeyCloakUrl, dcClientId, dcClientSecret); + model.addAttribute("sftp_post_endpoint_url", sftpPostEndpointUrl); + model.addAttribute("sftp_dc_data_endpoint_url", sftpDcDataEndpointUrl); + model.addAttribute("sftp_dp1_data_endpoint_url", sftpDp1DataEndpointUrl); + model.addAttribute("sftp_dp2_data_endpoint_url", sftpDp2DataEndpointUrl); + model.addAttribute("clear_dc_db_endpoint_url", clearDcDbEndpointUrl); + model.addAttribute("jwtToken", jwtToken); + return "dashboardSftpSse"; + } +```` + +49. Create method createStatusRequestSftp() for creating endpoint to listen to CSV file payload to handle using SFTP in DcController. +```` + @Operation(summary = "Listen to CSV file payload to handle using SFTP") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping(value = "/public/api/v1/consumer/search/sftp/csv") + public AcknowledgementDTO createStatusRequestSftp(@RequestParam("file") MultipartFile file) { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + try { + if (!Objects.equals(file.getContentType(), "text/csv")) { + acknowledgementDTO.setStatus(HeaderStatusENUM.RJCT.toValue()); + acknowledgementDTO.setMessage("Invalid file type"); + return acknowledgementDTO; + } + String originalFilename = UUID.randomUUID() + ".csv"; + Path tempFile = Paths.get(System.getProperty("java.io.tmpdir"), originalFilename); + Files.createFile(tempFile); + file.transferTo(tempFile.toFile()); + SftpServerConfigDTO sftpServerConfigDTO = commonUtils.getSftpServerConfigDTO(); + Boolean status = sftpHandlerService.uploadFileToSftp(sftpServerConfigDTO, tempFile.toString(), + sftpServerConfigDTO.getRemoteInboundDirectory()); + Files.delete(tempFile); + if (Boolean.FALSE.equals(status)) { + acknowledgementDTO.setStatus(HeaderStatusENUM.RJCT.toValue()); + acknowledgementDTO.setMessage(Constants.UPLOAD_ERROR); + return acknowledgementDTO; + } + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + acknowledgementDTO.setMessage("File uploaded successfully"); + } catch (IOException e) { + log.error(Constants.UPLOAD_ERROR, e); + acknowledgementDTO.setStatus(HeaderStatusENUM.RJCT.toValue()); + acknowledgementDTO.setMessage(Constants.UPLOAD_ERROR); + } + return acknowledgementDTO; + } +```` +50. Import below curl to run the endpoint and add payload.csv +```` +curl --location 'localhost:8000/public/api/v1/consumer/search/sftp/csv' \ +--form 'file=@"/home/ttpl-rt-119/Downloads/payload.csv"' +```` +51. Add below 2 methods in DcController. +```` + @GetMapping("/dashboard/leftPanel/data") + public List fetchLeftPanelData() { + List leftPanelDataDTOList = new ArrayList<>(); + Optional> optionalList = + responseTrackerRepository.findAllByAction("search"); + if (optionalList.isEmpty()) { + return leftPanelDataDTOList; + } + List responseTrackerEntityList = optionalList.get(); + for (ResponseTrackerEntity responseTrackerEntity : responseTrackerEntityList) { + HttpsLeftPanelDataDTO leftPanelDataDTO = new HttpsLeftPanelDataDTO(); + leftPanelDataDTO.setMessageTs(responseTrackerEntity.getMessageTs()); + leftPanelDataDTO.setTransactionId(responseTrackerEntity.getTransactionId()); + leftPanelDataDTO.setStatus(responseTrackerEntity.getStatus()); + leftPanelDataDTOList.add(leftPanelDataDTO); + } + return leftPanelDataDTOList; + } + + @GetMapping(value = "/dashboard/sftp/dc/data", produces = "text/event-stream") + public SseEmitter sseEmitterFirstPanel() { + return dcSftpPushUpdateService.register(); + } +```` +52. Add interface DcSftpPushUpdateService in service class. +```` +package g2pc.ref.dc.client.service; + +public interface DcSftpPushUpdateService { + SseEmitter register(); + + void pushUpdate(Object update); + + +} +```` +53. Implement DcSftpPushUpdateServiceImpl from DcSftpPushUpdateService , refer below code. +```` +@Service +@Slf4j +public class DcSftpPushUpdateServiceImpl implements DcSftpPushUpdateService { + private final List emitters = new ArrayList<>(); + public SseEmitter register() { + int minutes = 15; + long timeout = (long) minutes * 60000; + SseEmitter emitter = new SseEmitter(timeout); + this.emitters.add(emitter); + emitter.onCompletion(() -> this.emitters.remove(emitter)); + emitter.onTimeout(() -> this.emitters.remove(emitter)); + log.info("SSE emitter registered" + emitter); + return emitter; + } + public void pushUpdate(Object update) { + List deadEmitters = new ArrayList<>(); + this.emitters.forEach(emitter -> { + try { + emitter.send(update); + } catch (IOException e) { + deadEmitters.add(emitter); + } + }); + this.emitters.removeAll(deadEmitters); + } + public SftpDcData buildSftpDcData(String transactionId, String filename) { + SftpDcData sftpDcData = new SftpDcData(); + sftpDcData.setMessageTs(CommonUtils.getCurrentTimeStamp()); + sftpDcData.setTransactionId(transactionId); + sftpDcData.setFileName(filename); + sftpDcData.setSftpDirectoryType("INBOUND"); + return sftpDcData; + } +} +```` +54. Add dto HttpsLeftPanelDataDTO in package g2pc.ref.dc.client.dto.dashboard +```` +@Data +@AllArgsConstructor +@NoArgsConstructor +public class HttpsLeftPanelDataDTO { + + private String messageTs; + private String transactionId; + private String status; +} +```` +55. Add SftpDcData dto in package g2pc.ref.dc.client.dto.dashboard +```` +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SftpDcData { + + private String messageTs; + private String transactionId; + private String fileName; + private String sftpDirectoryType; +} +```` + +# 8. Keycloak configuration +### Steps for DC and DP - +1. Create data-consumer/data-provider realm. Click on Add realm shown below. +![Alt-text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-realm-creation.png) +2. Enter appropriate name in small cases. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-add-realm.png) +3. Change token expiry time as per requirement of testing. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-token-expiry.png) +4. Create client. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-create-realm-button.png) +5. Add appropriate client name. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-create-client.png) +6. Select Access type is confidential , enable service account and Enable Authorization. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-client-auth-setting.png) +7. Refer below image for client secret +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-dc-client.png) +8. Enable above all setting for admin-cli client and admin cli of master realm as well. +9. Add below scopes in client scope of admin-cli of master realm. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-master-admin-cli-client-scope.png) +10. Add below scopes in scope of admin-cli of master realm. +![Alt text](https://github.com/G2P-Connect/g2pc-registry/blob/alpha-1.0/docs/src/images/keycloak-master-admin-cli-scope.png) + + +# SFTP configuration - +### Below are the steps to configure SFTP - +1. Run the below docker-compose.yml using below command. +```` +sudo docker compose up +```` +docker-compose.yml +```` +version: '3' +services: + dc1_sftp: + image: atmoz/sftp + container_name: dc1_sftp_server + restart: unless-stopped + ports: + - "2224:22" + command: cdpi:1234:::inbound,outbound + + dp1_sftp: + image: atmoz/sftp + container_name: dp1_sftp_server + restart: unless-stopped + ports: + - "2225:22" + command: cdpi:1234:::inbound,outbound + + dp2_sftp: + image: atmoz/sftp + container_name: dp2_sftp_server + restart: unless-stopped + ports: + - "2226:22" + command: cdpi:1234:::inbound,outbound +```` +2. Install any FTP client viewer. Refer below image. Add sites using below steps - + 1. Open client and select tab shown in below image. + ![Alt text](/home/ttpl-rt-119/Documents/CDPI/G2P-Code/Git_hub/g2pc-registry/docs/src/images/filezilla.png) + 2. Create site , select SFTP , add local host and port. Add username and password added in docker-compose file. + ![Alt text](/home/ttpl-rt-119/Documents/CDPI/G2P-Code/Git_hub/g2pc-registry/docs/src/images/filezilla-site-create.png) + 3. Create folder structure shown below. + ![Alt text](/home/ttpl-rt-119/Documents/CDPI/G2P-Code/Git_hub/g2pc-registry/docs/src/images/filezila-folder.png) + + + +# Sunbird Rc Integration +### Below are the steps to install sunbird rc in local host. +1. Refer below docker-compose.yml file. Change password and host name ad per instruction given in below file. +```` +version: '2.4' + +services: + db: + image: postgres + container_name: {Add container name of db} + restart: unless-stopped + ports: + - '{Add port}:5432' + environment: + - POSTGRES_DB={Add DB name} + - POSTGRES_USER={Add user name} + - POSTGRES_PASSWORD={Add password} + es: + image: docker.elastic.co/elasticsearch/elasticsearch:7.10.1 + container_name: {Add container name of es} + restart: unless-stopped + environment: + - discovery.type=single-node + - 'ES_JAVA_OPTS=-Xms512m -Xmx512m' + healthcheck: + test: ['CMD', 'curl', '-f', 'localhost:9200/_cluster/health'] + interval: 30s + timeout: 10s + retries: 4 + ports: + - '{Add port}:9200' + - '{Add port}:9300' + + rg: + image: dockerhub/sunbird-rc-core:v0.0.8 + container_name: {Add container name of sunbird rc} + restart: unless-stopped + volumes: + - /home/{Add machine hostname}/sunbirdrc/config/schemas:/home/sunbirdrc/config/public/_schemas + environment: + - connectionInfo_uri=jdbc:postgresql://db:5432/registry + - connectionInfo_username={Add username of connection of db} + - connectionInfo_password={Add password of connection of db} + - elastic_search_connection_url=es:{Add port like 9201} + - search_provider=dev.sunbirdrc.registry.service.ElasticSearchService + - search_providerName=dev.sunbirdrc.registry.service.ElasticSearchService + - signature_enabled=false + - authentication_enabled=false + ports: + - '{Add port to run registry like 8080,8081}:8081' + depends_on: + - db + - es +```` +2. Create folder structure mentioned below. +```` +/home/{Add machine hostname}/sunbirdrc/config/schemas +```` +3. +4. Open the terminal in the folder where docker-compose.yml is there. +5. Recreate and start the Elasticsearch container: +```` +sudo docker-compose up -d --no-deps --force-recreate es +```` +This command rebuilds and recreates the Elasticsearch container (g2pc-es-1), excluding its dependencies. +It ensures that the container starts with a fresh configuration. +6. create and start the database container: +```` +sudo docker-compose up -d --no-deps --force-recreate db +```` +This command rebuilds and recreates the database container (g2pc-db-1), excluding its dependencies. +It ensures that the container starts with a fresh configuration. +7. Recreate and start the Sunbird-RC registry container: +```` +sudo docker-compose up -d --no-deps --force-recreate rg +```` +This command rebuilds and recreates the Sunbird-RC registry container (g2pc-rg-1), excluding its dependencies. +It ensures that the container starts with a fresh configuration. +8. Check the running containers and Verify that the containers are up and running. +```` +sudo docker ps +```` +9. Adjust paths and container names accordingly based on your specific setup and configurations. +10. These commands use Docker Compose to manage and orchestrate the containers. +11. The --no-deps flag ensures that only the specified service is recreated without starting its dependencies. +12. The --force-recreate flag ensures the recreation of the container even if it is already running. +13. Use below command to restart the registry. +```` + sudo docker restart {container-name} +```` + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/g2pc-registry_design.drawio b/docs/g2pc-registry_design.drawio new file mode 100644 index 0000000..cf76415 --- /dev/null +++ b/docs/g2pc-registry_design.drawio @@ -0,0 +1,338 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/images/.p12-dc.png b/docs/src/images/.p12-dc.png new file mode 100644 index 0000000..ec5e767 Binary files /dev/null and b/docs/src/images/.p12-dc.png differ diff --git a/docs/src/images/.p12-dp.png b/docs/src/images/.p12-dp.png new file mode 100644 index 0000000..549df41 Binary files /dev/null and b/docs/src/images/.p12-dp.png differ diff --git a/docs/src/images/DC-DP-communication.png b/docs/src/images/DC-DP-communication.png new file mode 100644 index 0000000..5e38153 Binary files /dev/null and b/docs/src/images/DC-DP-communication.png differ diff --git a/docs/src/images/HeaderDTORelationship.png b/docs/src/images/HeaderDTORelationship.png new file mode 100644 index 0000000..074e23f Binary files /dev/null and b/docs/src/images/HeaderDTORelationship.png differ diff --git a/docs/src/images/Sign-encry-table.png b/docs/src/images/Sign-encry-table.png new file mode 100644 index 0000000..cf0293c Binary files /dev/null and b/docs/src/images/Sign-encry-table.png differ diff --git a/docs/src/images/dc-on-search-sequence-dia.png b/docs/src/images/dc-on-search-sequence-dia.png new file mode 100644 index 0000000..6de88b9 Binary files /dev/null and b/docs/src/images/dc-on-search-sequence-dia.png differ diff --git a/docs/src/images/dc-package-structure.png b/docs/src/images/dc-package-structure.png new file mode 100644 index 0000000..e4226db Binary files /dev/null and b/docs/src/images/dc-package-structure.png differ diff --git a/docs/src/images/dc-seq-diagram-2.png b/docs/src/images/dc-seq-diagram-2.png new file mode 100644 index 0000000..8a0402b Binary files /dev/null and b/docs/src/images/dc-seq-diagram-2.png differ diff --git a/docs/src/images/dp-package-strcuture.png b/docs/src/images/dp-package-strcuture.png new file mode 100644 index 0000000..27d5d5c Binary files /dev/null and b/docs/src/images/dp-package-strcuture.png differ diff --git a/docs/src/images/dp-scheduler-seq-diagram.png b/docs/src/images/dp-scheduler-seq-diagram.png new file mode 100644 index 0000000..476c4a4 Binary files /dev/null and b/docs/src/images/dp-scheduler-seq-diagram.png differ diff --git a/docs/src/images/dp-scheduler-sequence-dia.png b/docs/src/images/dp-scheduler-sequence-dia.png new file mode 100644 index 0000000..547113b Binary files /dev/null and b/docs/src/images/dp-scheduler-sequence-dia.png differ diff --git a/docs/src/images/dp-search-sequence-diagram.png b/docs/src/images/dp-search-sequence-diagram.png new file mode 100644 index 0000000..2522d24 Binary files /dev/null and b/docs/src/images/dp-search-sequence-diagram.png differ diff --git a/docs/src/images/dp-specs-json.png b/docs/src/images/dp-specs-json.png new file mode 100644 index 0000000..6bb80c1 Binary files /dev/null and b/docs/src/images/dp-specs-json.png differ diff --git a/docs/src/images/dp-specs.png b/docs/src/images/dp-specs.png new file mode 100644 index 0000000..b3bee5a Binary files /dev/null and b/docs/src/images/dp-specs.png differ diff --git a/docs/src/images/dp_schema.png b/docs/src/images/dp_schema.png new file mode 100644 index 0000000..04850f7 Binary files /dev/null and b/docs/src/images/dp_schema.png differ diff --git a/docs/src/images/filezila-folder.png b/docs/src/images/filezila-folder.png new file mode 100644 index 0000000..0807cd5 Binary files /dev/null and b/docs/src/images/filezila-folder.png differ diff --git a/docs/src/images/filezilla-site-create.png b/docs/src/images/filezilla-site-create.png new file mode 100644 index 0000000..d8c3914 Binary files /dev/null and b/docs/src/images/filezilla-site-create.png differ diff --git a/docs/src/images/filezilla.png b/docs/src/images/filezilla.png new file mode 100644 index 0000000..bf99079 Binary files /dev/null and b/docs/src/images/filezilla.png differ diff --git a/docs/src/images/keycloak-add-realm.png b/docs/src/images/keycloak-add-realm.png new file mode 100644 index 0000000..ac0d84d Binary files /dev/null and b/docs/src/images/keycloak-add-realm.png differ diff --git a/docs/src/images/keycloak-client-auth-setting.png b/docs/src/images/keycloak-client-auth-setting.png new file mode 100644 index 0000000..e8c8fac Binary files /dev/null and b/docs/src/images/keycloak-client-auth-setting.png differ diff --git a/docs/src/images/keycloak-create-client.png b/docs/src/images/keycloak-create-client.png new file mode 100644 index 0000000..dac8d91 Binary files /dev/null and b/docs/src/images/keycloak-create-client.png differ diff --git a/docs/src/images/keycloak-create-realm-button.png b/docs/src/images/keycloak-create-realm-button.png new file mode 100644 index 0000000..52a0137 Binary files /dev/null and b/docs/src/images/keycloak-create-realm-button.png differ diff --git a/docs/src/images/keycloak-dc-client.png b/docs/src/images/keycloak-dc-client.png new file mode 100644 index 0000000..14cf285 Binary files /dev/null and b/docs/src/images/keycloak-dc-client.png differ diff --git a/docs/src/images/keycloak-dp-client-secret.png b/docs/src/images/keycloak-dp-client-secret.png new file mode 100644 index 0000000..654b796 Binary files /dev/null and b/docs/src/images/keycloak-dp-client-secret.png differ diff --git a/docs/src/images/keycloak-master-admin-cli-client-scope.png b/docs/src/images/keycloak-master-admin-cli-client-scope.png new file mode 100644 index 0000000..0a11a78 Binary files /dev/null and b/docs/src/images/keycloak-master-admin-cli-client-scope.png differ diff --git a/docs/src/images/keycloak-master-admin-cli-scope.png b/docs/src/images/keycloak-master-admin-cli-scope.png new file mode 100644 index 0000000..0a0d432 Binary files /dev/null and b/docs/src/images/keycloak-master-admin-cli-scope.png differ diff --git a/docs/src/images/keycloak-realm-creation.png b/docs/src/images/keycloak-realm-creation.png new file mode 100644 index 0000000..1cc54ef Binary files /dev/null and b/docs/src/images/keycloak-realm-creation.png differ diff --git a/docs/src/images/keycloak-token-expiry.png b/docs/src/images/keycloak-token-expiry.png new file mode 100644 index 0000000..c218f53 Binary files /dev/null and b/docs/src/images/keycloak-token-expiry.png differ diff --git a/docs/src/images/on_search_seqeunce_dia.png b/docs/src/images/on_search_seqeunce_dia.png new file mode 100644 index 0000000..1830e63 Binary files /dev/null and b/docs/src/images/on_search_seqeunce_dia.png differ diff --git a/docs/src/images/on_search_spec.png b/docs/src/images/on_search_spec.png new file mode 100644 index 0000000..b6e31ef Binary files /dev/null and b/docs/src/images/on_search_spec.png differ diff --git a/docs/src/images/patload_postman.png b/docs/src/images/patload_postman.png new file mode 100644 index 0000000..126e8f9 Binary files /dev/null and b/docs/src/images/patload_postman.png differ diff --git a/docs/src/images/payload_sequence_diagram.png b/docs/src/images/payload_sequence_diagram.png new file mode 100644 index 0000000..929a94f Binary files /dev/null and b/docs/src/images/payload_sequence_diagram.png differ diff --git a/docs/src/images/search-endpoint-spec.png b/docs/src/images/search-endpoint-spec.png new file mode 100644 index 0000000..a739a0a Binary files /dev/null and b/docs/src/images/search-endpoint-spec.png differ diff --git a/docs/src/images/search_sequence_diagram.png b/docs/src/images/search_sequence_diagram.png new file mode 100644 index 0000000..7bbb32a Binary files /dev/null and b/docs/src/images/search_sequence_diagram.png differ diff --git a/docs/src/images/spring_boot_dc_creation.png b/docs/src/images/spring_boot_dc_creation.png new file mode 100644 index 0000000..44d4380 Binary files /dev/null and b/docs/src/images/spring_boot_dc_creation.png differ diff --git a/docs/src/images/spring_boot_dp_creation.png b/docs/src/images/spring_boot_dp_creation.png new file mode 100644 index 0000000..08ebdaa Binary files /dev/null and b/docs/src/images/spring_boot_dp_creation.png differ diff --git a/g2pc-core-lib/.gitignore b/g2pc-core-lib/.gitignore new file mode 100644 index 0000000..e23d88f --- /dev/null +++ b/g2pc-core-lib/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +mvnw +mvnw.cmd +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +.idea/ + diff --git a/g2pc-core-lib/README.md b/g2pc-core-lib/README.md new file mode 100644 index 0000000..f1b8410 --- /dev/null +++ b/g2pc-core-lib/README.md @@ -0,0 +1,73 @@ +# G2pc Core Lib + +## JSON schema validations +This project is an implementation of the JSON Schema Draft v4, + +### When to use Json schema +Let'+s assume that you already know what JSON Schema is, +and you want to utilize it in a Java application to validate JSON data. But - as you may have already discovered - there is also an other Java implementation of the JSON Schema specification. So here are some advices about which one to use: + +1. if you use Jackson to handle JSON in Java code, then java-json-tools/json-schema-validator is obviously a better choice, since it uses Jackson +2. if you want to use the org.json API then this library is the better choice +3. if you need JSON Schema Draft 6 / 7 support, then you need this library. + +### Maven Dependency +```` + + com.github.erosb + everit-json-schema + 1.14.2 + +```` + +### Where we used the json schema +* We have 2 end-points , /search and /on-search. +* In these end-points we are receiving 2 payloads respectively. +* Each payload had header and message. +* We are using JSON schema to validate the header and message as per G2p specifications. + +#### Below are some samples schema which written in this project. +```` +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "header schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "version" : { + "type": [ "string", "null" ] + }, + }, + "total_count": { + "type": "number" + }, + "is_msg_encrypted": { + "type": ["boolean","null"], + "default": "false" + }, + "meta": { + "type": [ "object", "null" ] + } + }, + "required": ["message_id","message_ts","action","sender_id","total_count"], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} +```` + +Using below code we can read the above schema json +```` +InputStream schemaStream = CommonUtils.class.getClassLoader() + .getResourceAsStream("schema/ResponseMessageschema.json"); +```` + + diff --git a/g2pc-core-lib/pom.xml b/g2pc-core-lib/pom.xml new file mode 100644 index 0000000..a7878aa --- /dev/null +++ b/g2pc-core-lib/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.0.12 + + + g2pc.core.lib + g2pc-core-library + 0.0.1-SNAPSHOT + g2p-core + Common-g2pc-specifications-library + + 17 + + + + org.projectlombok + lombok + true + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.springframework.boot + spring-boot-starter-web + + + org.yaml + snakeyaml + 2.2 + + + com.networknt + json-schema-validator + 1.0.82 + + + javax.validation + validation-api + 2.0.1.Final + + + javax.annotation + javax.annotation-api + 1.3.2 + + + org.springframework.security + spring-security-web + 6.2.0 + + + com.auth0 + java-jwt + 4.4.0 + + + jakarta.validation + jakarta.validation-api + 3.0.2 + + + org.springframework.security + spring-security-config + 6.1.2 + + + com.konghq + unirest-java + 3.14.2 + + + redis.clients + jedis + 5.0.0-alpha2 + jar + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.apache.commons + commons-lang3 + 3.14.0 + + + + com.jcraft + jsch + 0.1.55 + + + org.springframework.integration + spring-integration-sftp + + + org.springframework.integration + spring-integration-core + + + org.apache.commons + commons-csv + 1.10.0 + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + 7.10.2 + + + diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/config/ElasticsearchConfig.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/ElasticsearchConfig.java new file mode 100644 index 0000000..d47b6c3 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/ElasticsearchConfig.java @@ -0,0 +1,30 @@ +package g2pc.core.lib.config; + +import org.apache.http.HttpHost; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestHighLevelClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ElasticsearchConfig { + + @Value("${sunbird.elasticsearch.host}") + private String elasticsearchHost; + + @Value("${sunbird.elasticsearch.port}") + private int elasticsearchPort; + + @Value("${sunbird.elasticsearch.scheme}") + private String elasticsearchScheme; + + @Bean + public RestHighLevelClient client() { + return new RestHighLevelClient( + RestClient.builder( + new HttpHost(elasticsearchHost, elasticsearchPort, elasticsearchScheme) + ) + ); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/config/G2pUnirest.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/G2pUnirest.java new file mode 100644 index 0000000..4105a43 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/G2pUnirest.java @@ -0,0 +1,23 @@ +package g2pc.core.lib.config; + + +import kong.unirest.GetRequest; +import kong.unirest.HttpRequestWithBody; +import kong.unirest.UnirestException; + +public interface G2pUnirest { + + String getG2pApiCall(String uri, String token) throws UnirestException; + + String getG2pApiCall(String uri) throws UnirestException; + + HttpRequestWithBody g2pPost(String uri); + + HttpRequestWithBody g2pPut(String uri); + + HttpRequestWithBody g2pDelete(String uri); + + HttpRequestWithBody g2pPatch(String uri); + + GetRequest g2pGet(String uri); +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/config/G2pUnirestHelper.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/G2pUnirestHelper.java new file mode 100644 index 0000000..8b4266e --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/G2pUnirestHelper.java @@ -0,0 +1,82 @@ +package g2pc.core.lib.config; + + +import g2pc.core.lib.security.context.UnirestContext; +import kong.unirest.GetRequest; +import kong.unirest.HttpRequestWithBody; +import kong.unirest.Unirest; +import kong.unirest.UnirestException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.util.Collections; +import java.util.Map; + +@Configuration +@Slf4j +@EnableScheduling +@Lazy +public class G2pUnirestHelper implements G2pUnirest { + + private static final String AUTH = "Authorization"; + + + protected GetRequest setG2pHeaders(GetRequest request) { + return setG2pHeaders(request, Collections.emptyMap()); + } + + protected GetRequest setG2pHeaders(GetRequest request, Map keyVal) { + if (null != keyVal) + keyVal.forEach((k, v) -> request.header(k, v)); + return request; + } + + protected HttpRequestWithBody setG2pHeaders(HttpRequestWithBody request) { + UnirestContext unirestContext = new UnirestContext(); + if (null != unirestContext && StringUtils.isNotBlank(unirestContext.getJwtHeader())) { + request.header(AUTH, unirestContext.getJwtHeader()); + } + return request; + } + + public String getG2pApiCall(String uri, String token) throws UnirestException { + + return setG2pHeaders(Unirest.get(uri), Map.of(AUTH, "Bearer " + token)) + .asString() + .getBody(); + + } + + public String getG2pApiCall(String uri) throws UnirestException { + + return setG2pHeaders(Unirest.get(uri)) + .asString() + .getBody(); + + } + + public HttpRequestWithBody g2pPost(String uri) { + return setG2pHeaders(Unirest.post(uri)); + } + + public HttpRequestWithBody g2pPut(String uri) { + return setG2pHeaders(Unirest.put(uri)); + } + + public HttpRequestWithBody g2pDelete(String uri) { + return setG2pHeaders(Unirest.delete(uri)); + } + + public HttpRequestWithBody g2pPatch(String uri) { + return setG2pHeaders(Unirest.patch(uri)); + } + + public GetRequest g2pGet(String uri) { + return setG2pHeaders(Unirest.get(uri)); + } + +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/config/RedisConfig.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/RedisConfig.java new file mode 100644 index 0000000..ca482a3 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/RedisConfig.java @@ -0,0 +1,64 @@ +package g2pc.core.lib.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +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.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericToStringSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import redis.clients.jedis.JedisPoolConfig; + +@Configuration +@Slf4j +public class RedisConfig { + + @Value("${spring.data.redis.host}") + private String redisHost; + + @Value("${spring.data.redis.port}") + private int redisPort; + + @Value("${spring.data.redis.password}") + private String redisPassword; + + public RedisConnectionFactory redisConnectionFactory() { + + JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); + jedisPoolConfig.setMaxTotal(10); + jedisPoolConfig.setTestOnBorrow(true); + jedisPoolConfig.setTestOnReturn(true); + + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisHost, redisPort); + + log.info("Redis connection using redisHost: {}, redisPort: {}", redisHost, redisPort); + + redisStandaloneConfiguration.setHostName(redisHost); + if (redisPassword != null && !redisPassword.trim().isEmpty()) { + log.info("redisPassword set for Redis: {}", redisPassword); + redisStandaloneConfiguration.setPassword(redisPassword); + } + redisStandaloneConfiguration.setPort(redisPort); + + JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); + jedisConnectionFactory.setPoolConfig(jedisPoolConfig); + + jedisConnectionFactory.afterPropertiesSet(); + + return jedisConnectionFactory; + } + + @Bean + RedisTemplate redisTemplate() { + final RedisTemplate redisTemplate = new RedisTemplate<>(); + log.info("String template used by Redis..."); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new GenericToStringSerializer<>(String.class)); + redisTemplate.setValueSerializer(new GenericToStringSerializer<>(String.class)); + return redisTemplate; + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/config/SftpConfig.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/SftpConfig.java new file mode 100644 index 0000000..ad69f8b --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/config/SftpConfig.java @@ -0,0 +1,61 @@ +package g2pc.core.lib.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.InboundChannelAdapter; +import org.springframework.integration.annotation.Poller; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.core.MessageSource; +import org.springframework.integration.file.FileReadingMessageSource; +import org.springframework.integration.file.filters.AcceptOnceFileListFilter; +import org.springframework.messaging.MessageChannel; + +import java.io.File; + +@Configuration +@Slf4j +public class SftpConfig { + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpLocalDirectoryInbound; + + @Value("${sftp.listener.local.outbound_directory}") + private String sftpLocalDirectoryOutbound; + + @Bean + public MessageChannel sftpInbound() { + return new DirectChannel(); + } + + @Bean + public MessageChannel sftpOutbound() { + return new DirectChannel(); + } + + @Bean + public MessageChannel errorChannel() { + return new DirectChannel(); + } + + @Bean + @InboundChannelAdapter(channel = "sftpInbound", poller = @Poller(fixedDelay = "5000")) + public MessageSource fileReadingMessageSourceInbound() { + FileReadingMessageSource source = new FileReadingMessageSource(); + source.setDirectory(new File(sftpLocalDirectoryInbound)); + source.setAutoCreateDirectory(true); + source.setFilter(new AcceptOnceFileListFilter<>()); + return source; + } + + @Bean + @InboundChannelAdapter(channel = "sftpOutbound", poller = @Poller(fixedDelay = "5000")) + public MessageSource fileReadingMessageSourceOutbound() { + FileReadingMessageSource source = new FileReadingMessageSource(); + source.setDirectory(new File(sftpLocalDirectoryOutbound)); + source.setAutoCreateDirectory(true); + source.setFilter(new AcceptOnceFileListFilter<>()); + return source; + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/CoreConstants.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/CoreConstants.java new file mode 100644 index 0000000..af6a406 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/CoreConstants.java @@ -0,0 +1,92 @@ +package g2pc.core.lib.constants; + +public class CoreConstants { + + private CoreConstants() { + } + + public static final String CANNOT_DESERIALIZE_TYPE = "Cannot deserialize Type"; + + public static final String QUERY_NAME = "query_name"; + + public static final String DP_SEARCH_URL = "dp_search_url"; + + public static final String DP_STATUS_URL = "dp_status_url"; + + public static final String REG_TYPE = "reg_type"; + + public static final String REG_SUB_TYPE = "reg_sub_type"; + + public static final String QUERY_TYPE = "query_type"; + + public static final String SORT_ATTRIBUTE = "sort_attribute"; + + public static final String SORT_ORDER = "sort_order"; + + public static final String PAGE_NUMBER = "page_number"; + + public static final String PAGE_SIZE = "page_size"; + + public static final String KEYCLOAK_URL = "keycloak_url"; + + public static final String KEYCLOAK_CLIENT_ID = "keycloak_client_id"; + + public static final String KEYCLOAK_CLIENT_SECRET = "keycloak_client_secret"; + + public static final String HASHING_ALGORITHM = "hashing_algorithm"; + + public static final String IS_SIGN = "is_sign"; + + public static final String DP_ID = "dp_id"; + + public static final String SUPPORT_ENCRYPTION = "support_encryption"; + + public static final String SUPPORT_SIGNATURE = "support_signature"; + + public static final String KEY_PATH = "key_path"; + + public static final String KEY_PASSWORD = "key_password"; + + public static final String DP_CLEAR_DB_URL = "dp_clear_db_url"; + + public static final String SEND_PROTOCOL_HTTPS = "https"; + + public static final String SEND_PROTOCOL_SFTP = "sftp"; + + public static final String SEARCH_TXN_TYPE = "search"; + + public static final String STATUS_TXN_TYPE = "status"; + + public static final String CONTENT_TYPE = "Content-Type"; + + public static final String GRANT_TYPE ="grant_type"; + + public static final String CLIENT_ID = "client_id"; + + public static final String CLIENT_SECRET= "client_secret"; + + public static final String USERNAME = "username"; + + public static final String PASSWORD = "password"; + + public static final String ACCESS_TOKEN = "access_token"; + + public static final String AUTHORIZATION = "Authorization"; + + public static final String TOKEN_TYPE = "token_type"; + + public static final String EXPIRES_IN = "expires_in"; + + public static final String RESPONSE_HEADER = "response_header"; + + public static final String SEARCH_RESPONSE = "search_response"; + + public static final String STATUS_RESPONSE = "status_response"; + + + public static final String REQUEST_HEADER = "request_header"; + + public static final String SEARCH_REQUEST = "search_request"; + + public static final String STATUS_REQUEST = "status_request"; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/G2pSecurityConstants.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/G2pSecurityConstants.java new file mode 100644 index 0000000..82e68cf --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/G2pSecurityConstants.java @@ -0,0 +1,11 @@ +package g2pc.core.lib.constants; + +public class G2pSecurityConstants { + + private G2pSecurityConstants() { + } + + public static final String TOKEN_HEADER = "Authorization"; + + public static final String SECRET_KEY= "00112233445566778899AABBCCDDEEFF"; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/SftpConstants.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/SftpConstants.java new file mode 100644 index 0000000..dc25bca --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/constants/SftpConstants.java @@ -0,0 +1,37 @@ +package g2pc.core.lib.constants; + +public class SftpConstants { + + private SftpConstants() { + } + + public static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking"; + + public static final String UPLOAD_ERROR_MESSAGE = "Error while uploading file to SFTP server"; + + public static final String UPLOAD_SUCCESS_MESSAGE = "File uploaded successfully to SFTP server"; + + public static final String SFTP_HOST = "sftp_host"; + + public static final String SFTP_PORT = "sftp_port"; + + public static final String SFTP_USER = "sftp_user"; + + public static final String SFTP_PASSWORD = "sftp_password"; + + public static final String SFTP_ALLOW_UNKNOWN_KEYS = "sftp_allow_unknown_keys"; + + public static final String SFTP_SESSION_CONFIG = "sftp_session_config"; + + public static final String SFTP_SESSION_CHANNEL = "sftp_session_channel"; + + public static final String SFTP_REMOTE_INBOUND_DIRECTORY = "sftp_remote_inbound_directory"; + + public static final String SFTP_REMOTE_OUTBOUND_DIRECTORY = "sftp_remote_outbound_directory"; + + public static final String SFTP_LOCAL_INBOUND_DIRECTORY = "sftp_local_inbound_directory"; + + public static final String SFTP_LOCAL_OUTBOUND_DIRECTORY = "sftp_local_outbound_directory"; + + public static final String SFTP = "sftp"; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/AcknowledgementDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/AcknowledgementDTO.java new file mode 100644 index 0000000..0a9d2a4 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/AcknowledgementDTO.java @@ -0,0 +1,18 @@ +package g2pc.core.lib.dto.common; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AcknowledgementDTO { + + @JsonProperty("message") + private Object message; + + @JsonProperty("status") + private String status; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/PurposeDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/PurposeDTO.java new file mode 100644 index 0000000..b99d41c --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/PurposeDTO.java @@ -0,0 +1,17 @@ +package g2pc.core.lib.dto.common; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PurposeDTO { + + private String text; + + private String code; + + private String refUri; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/cache/CacheDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/cache/CacheDTO.java new file mode 100644 index 0000000..089df30 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/cache/CacheDTO.java @@ -0,0 +1,27 @@ +package g2pc.core.lib.dto.common.cache; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CacheDTO { + + @JsonProperty("data") + private String data; + + @JsonProperty("status") + private String status; + + @JsonProperty("protocol") + private String protocol; + + @JsonProperty("created_date") + private String createdDate; + + @JsonProperty("last_updated_date") + private String lastUpdatedDate; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/HeaderDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/HeaderDTO.java new file mode 100644 index 0000000..b474fbe --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/HeaderDTO.java @@ -0,0 +1,53 @@ +package g2pc.core.lib.dto.common.header; + +import com.fasterxml.jackson.annotation.*; +import lombok.*; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, + property = "type") +@JsonSubTypes({ +}) +public abstract class HeaderDTO { + + private String version = "1.0.0"; + + @JsonProperty("message_id") + private String messageId; + + @JsonProperty("message_ts") + private String messageTs; + + private String action; + + @JsonProperty("total_count") + private Integer totalCount; + + @JsonProperty("sender_id") + private String senderId; + + @JsonProperty("receiver_id") + private String receiverId; + + @JsonProperty("is_msg_encrypted") + private Boolean isMsgEncrypted; + + private MetaDTO meta; + + public HeaderDTO() { + } + + public HeaderDTO(String messageId, + String messageTs, + String action, + Integer totalCount, + String senderId, + String receiverId, + Boolean isMsgEncrypted, + MetaDTO meta) { + + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/MetaDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/MetaDTO.java new file mode 100644 index 0000000..54e5029 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/MetaDTO.java @@ -0,0 +1,11 @@ +package g2pc.core.lib.dto.common.header; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MetaDTO { + + private Object data; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/RequestHeaderDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/RequestHeaderDTO.java new file mode 100644 index 0000000..0b6b451 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/RequestHeaderDTO.java @@ -0,0 +1,31 @@ +package g2pc.core.lib.dto.common.header; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.*; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@JsonTypeName("requestHeader") +public class RequestHeaderDTO extends HeaderDTO { + + @JsonProperty("sender_uri") + private String senderUri; + + public RequestHeaderDTO(String messageId, + String messageTs, + String action, + Integer totalCount, + String senderId, + String receiverId, + Boolean isMsgEncrypted, + MetaDTO meta, + String senderUri) { + + super(messageId, messageTs, action, totalCount, senderId, receiverId, isMsgEncrypted, meta); + this.senderUri = senderUri; + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/ResponseHeaderDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/ResponseHeaderDTO.java new file mode 100644 index 0000000..f29bb2d --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/header/ResponseHeaderDTO.java @@ -0,0 +1,45 @@ +package g2pc.core.lib.dto.common.header; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.*; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@JsonTypeName("responseHeader") +public class ResponseHeaderDTO extends HeaderDTO { + + private String status; + + @JsonProperty("status_reason_code") + private String statusReasonCode; + + @JsonProperty("status_reason_message") + private String statusReasonMessage; + + @JsonProperty("completed_count") + private Integer completedCount; + + public ResponseHeaderDTO(String messageId, + String messageTs, + String action, + Integer totalCount, + String senderId, + String receiverId, + Boolean isMsgEncrypted, + MetaDTO meta, + String status, + String statusReasonCode, + String statusReasonMessage, + Integer completedCount) { + + super(messageId, messageTs, action, totalCount, senderId, receiverId, isMsgEncrypted, meta); + this.status = status; + this.statusReasonCode=statusReasonCode; + this.statusReasonMessage=statusReasonMessage; + this.completedCount=completedCount; + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/security/G2pTokenResponse.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/security/G2pTokenResponse.java new file mode 100644 index 0000000..77f67d9 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/security/G2pTokenResponse.java @@ -0,0 +1,17 @@ +package g2pc.core.lib.dto.common.security; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class G2pTokenResponse { + + private String accessToken; + private String tokenType; + private String expiresIn; +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/security/TokenExpiryDto.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/security/TokenExpiryDto.java new file mode 100644 index 0000000..df716e6 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/common/security/TokenExpiryDto.java @@ -0,0 +1,21 @@ +package g2pc.core.lib.dto.common.security; + + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.sql.Timestamp; + +@Data +@Getter +@Setter +public class TokenExpiryDto { + + private String token; + + private String expiresIn; + + private Timestamp dateSaved; + +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/AuthorizeDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/AuthorizeDTO.java new file mode 100644 index 0000000..1f12617 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/AuthorizeDTO.java @@ -0,0 +1,16 @@ +package g2pc.core.lib.dto.search.message.request; + +import g2pc.core.lib.dto.common.PurposeDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AuthorizeDTO { + + private String ts; + + private PurposeDTO purpose; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/ConsentDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/ConsentDTO.java new file mode 100644 index 0000000..5340405 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/ConsentDTO.java @@ -0,0 +1,16 @@ +package g2pc.core.lib.dto.search.message.request; + +import g2pc.core.lib.dto.common.PurposeDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ConsentDTO { + + private String ts; + + private PurposeDTO purpose; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/QueryDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/QueryDTO.java new file mode 100644 index 0000000..ef777e0 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/QueryDTO.java @@ -0,0 +1,16 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class QueryDTO { + + @JsonProperty("query_name") + private String queryName; + + @JsonProperty("query_params") + private Object queryParams; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestDTO.java new file mode 100644 index 0000000..0b8abc7 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestDTO.java @@ -0,0 +1,22 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDTO { + + @JsonProperty("signature") + private String signature; + + @JsonProperty("header") + private HeaderDTO header; + + @JsonProperty("message") + private Object message; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestMessageDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestMessageDTO.java new file mode 100644 index 0000000..9fda76e --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestMessageDTO.java @@ -0,0 +1,20 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RequestMessageDTO { + + @JsonProperty("transaction_id") + private String transactionId; + + @JsonProperty("search_request") + private List searchRequest; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestPaginationDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestPaginationDTO.java new file mode 100644 index 0000000..324f32a --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/RequestPaginationDTO.java @@ -0,0 +1,18 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestPaginationDTO { + + @JsonProperty("page_size") + private int pageSize = 100; + + @JsonProperty("page_number") + private int pageNumber = 1; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/ResponseMessageDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/ResponseMessageDTO.java new file mode 100644 index 0000000..536af20 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/ResponseMessageDTO.java @@ -0,0 +1,28 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import g2pc.core.lib.dto.search.message.response.SearchResponseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ResponseMessageDTO { + + @JsonProperty("transaction_id") + private String transactionId; + + @JsonProperty("correlation_id") + private String correlationId; + + @JsonProperty("search_response") + private List searchResponse; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SearchCriteriaDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SearchCriteriaDTO.java new file mode 100644 index 0000000..ace88eb --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SearchCriteriaDTO.java @@ -0,0 +1,39 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SearchCriteriaDTO { + + @JsonProperty("version") + private String version; + + @JsonProperty("reg_type") + private String regType; + + @JsonProperty("reg_sub_type") + private String regSubType; + + @JsonProperty("query_type") + private String queryType; + + @JsonProperty("query") + private QueryDTO query; + + @JsonProperty("sort") + private List sort; + + @JsonProperty("pagination") + private RequestPaginationDTO pagination; + + @JsonProperty("consent") + private ConsentDTO consent; + + @JsonProperty("authorize") + private AuthorizeDTO authorize; +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SearchRequestDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SearchRequestDTO.java new file mode 100644 index 0000000..94603d0 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SearchRequestDTO.java @@ -0,0 +1,26 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SearchRequestDTO { + + @JsonProperty("reference_id") + private String referenceId; + + @JsonProperty("timestamp") + private String timestamp; + + @JsonProperty("search_criteria") + private SearchCriteriaDTO searchCriteria; + + @JsonProperty("locale") + private String locale; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SortDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SortDTO.java new file mode 100644 index 0000000..ed0ba39 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/request/SortDTO.java @@ -0,0 +1,18 @@ +package g2pc.core.lib.dto.search.message.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SortDTO { + + @JsonProperty("attribute_name") + private String attributeName="YOB"; + + @JsonProperty("sort_order") + private String sortOrder="asc"; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/DataDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/DataDTO.java new file mode 100644 index 0000000..bd6974f --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/DataDTO.java @@ -0,0 +1,25 @@ +package g2pc.core.lib.dto.search.message.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DataDTO { + + @JsonProperty("version") + private String version; + + @JsonProperty("reg_type") + private String regType; + + @JsonProperty("reg_sub_type") + private String regSubType; + + @JsonProperty("reg_record_type") + private String regRecordType; + + @JsonProperty("reg_records") + private Object regRecords; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/ResponseDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/ResponseDTO.java new file mode 100644 index 0000000..0a6187a --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/ResponseDTO.java @@ -0,0 +1,23 @@ +package g2pc.core.lib.dto.search.message.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ResponseDTO { + + @JsonProperty("signature") + private String signature; + + @JsonProperty("header") + private HeaderDTO header; + + @JsonProperty("message") + private Object message; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/ResponsePaginationDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/ResponsePaginationDTO.java new file mode 100644 index 0000000..6df0a70 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/ResponsePaginationDTO.java @@ -0,0 +1,21 @@ +package g2pc.core.lib.dto.search.message.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ResponsePaginationDTO { + + @JsonProperty("page_size") + private Integer pageSize; + + @JsonProperty("page_number") + private Integer pageNumber; + + @JsonProperty("total_count") + private Integer totalCount; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/SearchResponseDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/SearchResponseDTO.java new file mode 100644 index 0000000..c814e24 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/search/message/response/SearchResponseDTO.java @@ -0,0 +1,36 @@ +package g2pc.core.lib.dto.search.message.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SearchResponseDTO { + + @JsonProperty("reference_id") + private String referenceId; + + @JsonProperty("timestamp") + private String timestamp; + + @JsonProperty("status") + private String status; + + @JsonProperty("status_reason_code") + private String statusReasonCode; + + @JsonProperty("status_reason_message") + private String statusReasonMessage; + + @JsonProperty("data") + private DataDTO data; + + @JsonProperty("pagination") + private ResponsePaginationDTO pagination; + + @JsonProperty("locale") + private String locale; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/sftp/SftpServerConfigDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/sftp/SftpServerConfigDTO.java new file mode 100644 index 0000000..2e1edee --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/sftp/SftpServerConfigDTO.java @@ -0,0 +1,34 @@ +package g2pc.core.lib.dto.sftp; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SftpServerConfigDTO { + + private String host; + + private int port; + + private String user; + + private String password; + + private Boolean allowUnknownKeys; + + private String strictHostKeyChecking; + + private String sessionChannel = "sftp"; + + private String remoteInboundDirectory; + + private String remoteOutboundDirectory; + + private String localInboundDirectory; + + private String localOutboundDirectory; +} + diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/StatusRequestDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/StatusRequestDTO.java new file mode 100644 index 0000000..18c506a --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/StatusRequestDTO.java @@ -0,0 +1,22 @@ +package g2pc.core.lib.dto.status.message.request; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@AllArgsConstructor +@NoArgsConstructor +public class StatusRequestDTO { + + @JsonProperty("signature") + private String signature; + + @JsonProperty("header") + private HeaderDTO header; + + @JsonProperty("message") + private Object message; +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/StatusRequestMessageDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/StatusRequestMessageDTO.java new file mode 100644 index 0000000..e703e31 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/StatusRequestMessageDTO.java @@ -0,0 +1,20 @@ +package g2pc.core.lib.dto.status.message.request; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class StatusRequestMessageDTO { + + @JsonProperty("transaction_id") + private String transactionId; + + @JsonProperty("txnstatus_request") + private TxnStatusRequestDTO txnStatusRequest; +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/TxnStatusRequestDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/TxnStatusRequestDTO.java new file mode 100644 index 0000000..45c3145 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/request/TxnStatusRequestDTO.java @@ -0,0 +1,27 @@ +package g2pc.core.lib.dto.status.message.request; + + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class TxnStatusRequestDTO { + + @JsonProperty("txn_type") + private String txnType; + + @JsonProperty("attribute_type") + private String attributeType; + + @JsonProperty("attribute_value") + private Object attributeValue; + + @JsonProperty("locale") + private String locale; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/StatusResponseDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/StatusResponseDTO.java new file mode 100644 index 0000000..4dee4f9 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/StatusResponseDTO.java @@ -0,0 +1,23 @@ +package g2pc.core.lib.dto.status.message.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class StatusResponseDTO { + + @JsonProperty("signature") + private String signature; + + @JsonProperty("header") + private HeaderDTO header; + + @JsonProperty("message") + private Object message; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/StatusResponseMessageDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/StatusResponseMessageDTO.java new file mode 100644 index 0000000..36e5b58 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/StatusResponseMessageDTO.java @@ -0,0 +1,25 @@ +package g2pc.core.lib.dto.status.message.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class StatusResponseMessageDTO { + + @JsonProperty("transaction_id") + private String transactionId; + + @JsonProperty("correlation_id") + private String correlationId; + + @JsonProperty("txnstatus_response") + private TxnStatusResponseDTO txnStatusResponse; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/TxnStatusResponseDTO.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/TxnStatusResponseDTO.java new file mode 100644 index 0000000..c6a2eff --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/dto/status/message/response/TxnStatusResponseDTO.java @@ -0,0 +1,23 @@ +package g2pc.core.lib.dto.status.message.response; + + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class TxnStatusResponseDTO { + + + @JsonProperty("txn_type") + private String txnType; + + @JsonProperty("txn_status") + private Object txnStatus; + +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/ActionsENUM.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/ActionsENUM.java new file mode 100644 index 0000000..8106ab4 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/ActionsENUM.java @@ -0,0 +1,32 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum ActionsENUM { + + SEARCH, ON_SEARCH , STATUS , ON_STATUS; + + public String toValue() { + switch (this) { + case SEARCH: return "search"; + case ON_SEARCH: return "on-search"; + case STATUS: return "status"; + case ON_STATUS: return "on-status"; + } + return null; + } + + public static ActionsENUM forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "search": return SEARCH; + case "on-search": return ON_SEARCH; + case "status" : return STATUS; + case "on-status" : return ON_STATUS; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/AlgorithmENUM.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/AlgorithmENUM.java new file mode 100644 index 0000000..e3da60b --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/AlgorithmENUM.java @@ -0,0 +1,38 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + + +/** + * This enum class is used to return algorithm strings + */ +public enum AlgorithmENUM { + + ED25519, RSA , HMAC , SHA256 , AES; + + public String toValue() { + switch (this) { + case ED25519: return "Ed25519"; + case RSA: return "rsa"; + case HMAC: return "HmacSHA256"; + case SHA256:return "SHA-256"; + case AES : return "AES"; + } + return null; + } + + public static AlgorithmENUM forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "ed25519": return ED25519; + case "rsa": return RSA; + case "HmacSHA256" : return HMAC; + case "SHA-256" : return SHA256; + case "AES" : return AES; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/AttributeTypeEnum.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/AttributeTypeEnum.java new file mode 100644 index 0000000..5ab2074 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/AttributeTypeEnum.java @@ -0,0 +1,52 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum AttributeTypeEnum { + + /** + * "transaction_id" "reference_id_list" "correlation_id" "subscription_code_list" + */ + + + TRANSACTION_ID , + + REFERENCE_ID_LIST, + + CORRELATION_ID , + + SUBSCRIPTION_CODE_LIST; + + + public String toValue() { + switch (this) { + case TRANSACTION_ID: + return "transaction_id"; + case REFERENCE_ID_LIST: + return "reference_id_list"; + case CORRELATION_ID: + return "correlation_id"; + case SUBSCRIPTION_CODE_LIST: + return "subscription_code_list"; + } + return null; + } + + public static AttributeTypeEnum forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "transaction_id": + return TRANSACTION_ID; + case "reference_id_list": + return REFERENCE_ID_LIST; + case "correlation_id": + return CORRELATION_ID; + case "subscription_code_list": + return SUBSCRIPTION_CODE_LIST; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/ExceptionsENUM.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/ExceptionsENUM.java new file mode 100644 index 0000000..ea36d83 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/ExceptionsENUM.java @@ -0,0 +1,46 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; +import java.io.IOException; + +public enum ExceptionsENUM { + + + ERROR_SIGNATURE_INVALID , + ERROR_VERSION_NOT_VALID , + ERROR_ENCRYPTION_INVALID , + ERROR_USER_UNAUTHORIZED , + ERROR_BAD_REQUEST , + ERROR_SERVICE_UNAVAILABLE , + + ERROR_REQUEST_NOT_FOUND; + + public String toValue() { + switch (this) { + case ERROR_SIGNATURE_INVALID: return "err.signature.invalid"; + case ERROR_VERSION_NOT_VALID: return "err.version.not_supported"; + case ERROR_ENCRYPTION_INVALID: return "err.encryption.invalid"; + case ERROR_USER_UNAUTHORIZED: return "err.request.unauthorized"; + case ERROR_BAD_REQUEST: return "err.request.bad"; + case ERROR_SERVICE_UNAVAILABLE: return "err.service.unavailable"; + case ERROR_REQUEST_NOT_FOUND: return "err.request.not_found"; + + } + return null; + } + + public static ExceptionsENUM forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "err.signature.invalid": return ERROR_SIGNATURE_INVALID; + case "err.version.not_supported": return ERROR_VERSION_NOT_VALID; + case "err.encryption.invalid":return ERROR_ENCRYPTION_INVALID; + case "err.request.unauthorized" :return ERROR_USER_UNAUTHORIZED; + case "err.request.bad" :return ERROR_BAD_REQUEST; + case "err.service.unavailable" : return ERROR_SERVICE_UNAVAILABLE; + case "err.request.not_found" : return ERROR_REQUEST_NOT_FOUND ; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/HeaderStatusENUM.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/HeaderStatusENUM.java new file mode 100644 index 0000000..1574bd7 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/HeaderStatusENUM.java @@ -0,0 +1,43 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum HeaderStatusENUM { + + RCVD, + PDNG, + SUCC, + RJCT; + + public String toValue() { + switch (this) { + case RCVD: + return "rcvd"; + case PDNG: + return "pdng"; + case SUCC: + return "succ"; + case RJCT: + return "rjct"; + } + return null; + } + + public static HeaderStatusENUM forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "rcvd": + return RCVD; + case "pdng": + return PDNG; + case "succ": + return SUCC; + case "rjct": + return RJCT; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/HeaderStatusReasonCodeENUM.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/HeaderStatusReasonCodeENUM.java new file mode 100644 index 0000000..dd7e7d6 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/HeaderStatusReasonCodeENUM.java @@ -0,0 +1,63 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum HeaderStatusReasonCodeENUM { + + RJCT_VERSION_INVALID, + RJCT_MESSAGE_ID_DUPLICATE, + RJCT_MESSAGE_TS_INVALID, + RJCT_ACTION_INVALID, + RJCT_ACTION_NOT_SUPPORTED, + RJCT_TOTAL_COUNT_INVALID, + RJCT_TOTAL_COUNT_LIMIT_EXCEEDED, + RJCT_ERRORS_TOO_MANY; + + public String toValue() { + switch (this) { + case RJCT_VERSION_INVALID: + return "rjct.version.invalid"; + case RJCT_MESSAGE_ID_DUPLICATE: + return "rjct.message_id.duplicate"; + case RJCT_MESSAGE_TS_INVALID: + return "rjct.message_ts.invalid"; + case RJCT_ACTION_INVALID: + return "rjct.action.invalid"; + case RJCT_ACTION_NOT_SUPPORTED: + return "rjct.action.not_supported"; + case RJCT_TOTAL_COUNT_INVALID: + return "rjct.total_count.invalid"; + case RJCT_TOTAL_COUNT_LIMIT_EXCEEDED: + return "rjct.total_count.limit_exceeded"; + case RJCT_ERRORS_TOO_MANY: + return "rjct.errors.too_many"; + } + return null; + } + + public static HeaderStatusReasonCodeENUM forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "rjct.version.invalid": + return RJCT_VERSION_INVALID; + case "rjct.message_id.duplicate": + return RJCT_MESSAGE_ID_DUPLICATE; + case "rjct.message_ts.invalid": + return RJCT_MESSAGE_TS_INVALID; + case "rjct.action.invalid": + return RJCT_ACTION_INVALID; + case "rjct.action.not_supported": + return RJCT_ACTION_NOT_SUPPORTED; + case "rjct.total_count.invalid": + return RJCT_TOTAL_COUNT_INVALID; + case "rjct.total_count.limit_exceeded": + return RJCT_TOTAL_COUNT_LIMIT_EXCEEDED; + case "rjct.errors.too_many": + return RJCT_ERRORS_TOO_MANY; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/LocalesENUM.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/LocalesENUM.java new file mode 100644 index 0000000..78c2742 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/LocalesENUM.java @@ -0,0 +1,29 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum LocalesENUM { + + EN, // English + HI; // Hindi + + public String toValue() { + switch (this) { + case EN: return "en"; + case HI: return "hi"; + } + return null; + } + + public static LocalesENUM forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "en": return EN; + case "hi": return HI; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/QueryTypeEnum.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/QueryTypeEnum.java new file mode 100644 index 0000000..b9a7628 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/QueryTypeEnum.java @@ -0,0 +1,30 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum QueryTypeEnum { + + NAMEDQUERY , IDTYPE , PREDICATE ; + + public String toValue() { + switch (this) { + case NAMEDQUERY: return "namedQuery"; + case IDTYPE: return "idtype"; + case PREDICATE:return "predicate"; + } + return null; + } + + public static QueryTypeEnum forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "namedQuery": return NAMEDQUERY; + case "idtype": return IDTYPE; + case "predicate":return PREDICATE; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/SortOrderEnum.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/SortOrderEnum.java new file mode 100644 index 0000000..51ac0a8 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/SortOrderEnum.java @@ -0,0 +1,28 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum SortOrderEnum { + + ASC , DESC ; + + public String toValue() { + switch (this) { + case ASC: return "asc"; + case DESC: return "desc"; + } + return null; + } + + public static SortOrderEnum forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "asc": return ASC; + case "desc": return DESC; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/StatusTransactionTypeEnum.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/StatusTransactionTypeEnum.java new file mode 100644 index 0000000..6bf3a7f --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/enums/StatusTransactionTypeEnum.java @@ -0,0 +1,54 @@ +package g2pc.core.lib.enums; + +import g2pc.core.lib.constants.CoreConstants; + +import java.io.IOException; + +public enum StatusTransactionTypeEnum { + + + /** + * "search" "subscribe" "unsubscribe" + */ + + SEARCH , SUBSCRIBE , UNSUBSCRIBE , ON_SEARCH , ON_SUBSCRIBE , ON_UNSUBSCRIBE; + + + public String toValue() { + switch (this) { + case SEARCH: + return "search"; + case SUBSCRIBE: + return "subscribe"; + case UNSUBSCRIBE: + return "unsubscribe"; + case ON_SEARCH: + return "on-search"; + case ON_SUBSCRIBE: + return "on-subscribe"; + case ON_UNSUBSCRIBE: + return "on-unsubscribe"; + } + return null; + } + + public static StatusTransactionTypeEnum forValue(String value) throws IOException { + if (null != value) { + switch (value.toLowerCase()) { + case "search": + return SEARCH; + case "subscribe": + return SUBSCRIBE; + case "unsubscribe": + return UNSUBSCRIBE; + case "on-search": + return ON_SEARCH; + case "on-subscribe": + return ON_SUBSCRIBE; + case "on-unsubscribe": + return ON_UNSUBSCRIBE; + } + } + throw new IOException(CoreConstants.CANNOT_DESERIALIZE_TYPE); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/ErrorResponse.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/ErrorResponse.java new file mode 100644 index 0000000..af4b51e --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/ErrorResponse.java @@ -0,0 +1,24 @@ +package g2pc.core.lib.exceptionhandler; + +import g2pc.core.lib.exceptions.G2pcError; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +public class ErrorResponse { + private G2pcError g2PcError; + + /** + * Instantiates a new Error response. + * + * @param g2PcError the error list + */ + public ErrorResponse(G2pcError g2PcError) + { + super(); + this.g2PcError = g2PcError; + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/GlobalExceptionHandler.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/GlobalExceptionHandler.java new file mode 100644 index 0000000..637b727 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/GlobalExceptionHandler.java @@ -0,0 +1,30 @@ +package g2pc.core.lib.exceptionhandler; + +import g2pc.core.lib.exceptions.G2pcValidationException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * The type Global exception handler. + */ +@ControllerAdvice +public class GlobalExceptionHandler { + + /** + * Handle exception error response. + * + * @param ex the ValidationExeption + * @return the error response + */ + @ExceptionHandler(value + = G2pcValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public @ResponseBody ValidationErrorResponse + handleException(G2pcValidationException ex) + { + return new ValidationErrorResponse(ex.getG2PcErrorList()); + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/ValidationErrorResponse.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/ValidationErrorResponse.java new file mode 100644 index 0000000..208aa93 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptionhandler/ValidationErrorResponse.java @@ -0,0 +1,27 @@ +package g2pc.core.lib.exceptionhandler; + +import g2pc.core.lib.exceptions.G2pcError; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * The type Error response. + */ +@Data +@NoArgsConstructor +public class ValidationErrorResponse { + private List g2PcErrors; + + /** + * Instantiates a new Error response. + * + * @param g2PcErrorList the error list + */ + public ValidationErrorResponse(List g2PcErrorList) + { + super(); + this.g2PcErrors = g2PcErrorList; + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pHttpException.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pHttpException.java new file mode 100644 index 0000000..8c92263 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pHttpException.java @@ -0,0 +1,26 @@ +package g2pc.core.lib.exceptions; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + + +@Data +@Getter +@Setter +public class G2pHttpException extends Exception{ + + private G2pcError g2PcError; + + /** + * Instantiates a new Validation exception. + * + * @param g2PcError the error list + */ + public G2pHttpException(G2pcError g2PcError){ + this.g2PcError = g2PcError; + + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pcError.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pcError.java new file mode 100644 index 0000000..7840919 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pcError.java @@ -0,0 +1,21 @@ +package g2pc.core.lib.exceptions; + + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +/** + * The type Error. + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +public class G2pcError { + + private String code ; + + private String message; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pcValidationException.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pcValidationException.java new file mode 100644 index 0000000..7c0e2e0 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/G2pcValidationException.java @@ -0,0 +1,29 @@ +package g2pc.core.lib.exceptions; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + + +/** + * The type Validation exception. + */ +@Data +@Getter +@Setter +public class G2pcValidationException extends Exception{ + + private List g2PcErrorList; + + /** + * Instantiates a new Validation exception. + * + * @param g2PcErrorList the error list + */ + public G2pcValidationException(List g2PcErrorList){ + this.g2PcErrorList = g2PcErrorList; + + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/InvalidTokenCustomException.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/InvalidTokenCustomException.java new file mode 100644 index 0000000..098c0b8 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/exceptions/InvalidTokenCustomException.java @@ -0,0 +1,26 @@ +package g2pc.core.lib.exceptions; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +@Getter +@Setter +/** + * The type Token is not valid exception. + */ +public class InvalidTokenCustomException extends Exception{ + + private G2pcError g2PcError; + + /** + * Instantiates a new Token is not valid exception. + * + * @param g2PcError the message + */ + public InvalidTokenCustomException(G2pcError g2PcError){ + this.g2PcError = g2PcError; + + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/BearerTokenUtil.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/BearerTokenUtil.java new file mode 100644 index 0000000..d3178e8 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/BearerTokenUtil.java @@ -0,0 +1,11 @@ +package g2pc.core.lib.security; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +public class BearerTokenUtil { + + public static String getBearerTokenHeader() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization"); + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/G2pTokenVerification.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/G2pTokenVerification.java new file mode 100644 index 0000000..e44e6f9 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/G2pTokenVerification.java @@ -0,0 +1,91 @@ +package g2pc.core.lib.security; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.Claim; +import g2pc.core.lib.constants.G2pSecurityConstants; +import g2pc.core.lib.security.service.G2pTokenService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.filter.OncePerRequestFilter; +import java.io.IOException; +import java.util.Date; +import com.auth0.jwt.interfaces.DecodedJWT; + +/** + * The G2p token verification. + */ +@Slf4j +public class G2pTokenVerification extends OncePerRequestFilter { + + @Autowired + G2pTokenService g2pTokenService; + + /** + * Method to validate token + * @param httpRequest httpRequest + * @param httpResponse httpResponse + * @param filterChain filterChain + * @throws ServletException ServletException + * @throws IOException IOException + */ + @Override + protected void doFilterInternal(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain filterChain) throws ServletException, IOException + { + String stringToken = httpRequest.getHeader(G2pSecurityConstants.TOKEN_HEADER); + + if(null != stringToken) { + if (stringToken.trim().equals("undefined")) { + log.warn(" Invalid token received with undefined value for path:'{}'", httpRequest.getRequestURI()); + filterChain.doFilter(httpRequest, httpResponse); + return; + } try { + stringToken = stringToken.replaceAll("Bearer ", ""); + DecodedJWT g2pDecodedJWT = JWT.decode(stringToken); + + if (g2pDecodedJWT.getExpiresAt().before(new Date())) { + log.warn("Token Expired:{}", httpRequest.getRequestURI()); + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpResponse.getWriter().println("Token Expired"); + return; + } else { + log.info("Setting context details:{}", httpRequest.getRequestURI()); + try { + Claim preferredUserNameClaim = g2pDecodedJWT.getClaim("preferred_username"); + String path = httpRequest.getRequestURI(); + if (preferredUserNameClaim.isMissing() || preferredUserNameClaim.isNull()) { + log.warn("Invalid userNameClaim in token:{}", g2pDecodedJWT.getToken()); + throw new IllegalStateException("Invalid Token"); + } + if (preferredUserNameClaim.isMissing() && path != null && path.contains("/private/")) { + log.warn("Invalid httpRequest made for path: {}", path); + throw new IllegalStateException("Unauthorized"); + } + + } catch (IllegalStateException e) { + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpResponse.getWriter().println("Login Required"); + return; + } + } + } catch (Exception e) { + log.error("Authentication Failure For Token: `{}` Path:`{}` error:{}", stringToken, httpRequest.getRequestURI(), e.getMessage(), e); + httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid Token"); + return; + } + } else { + if (httpRequest.getRequestURI().contains("/private/")) { + log.warn("Invalid httpRequest made for path: {}", httpRequest.getRequestURI()); + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpResponse.getWriter().println("Login Required"); + return; + } + } + filterChain.doFilter(httpRequest, httpResponse); + } + + +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/RandomIVGenerator.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/RandomIVGenerator.java new file mode 100644 index 0000000..20703f0 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/RandomIVGenerator.java @@ -0,0 +1,20 @@ +package g2pc.core.lib.security; + +import javax.crypto.spec.IvParameterSpec; +import java.security.SecureRandom; + +/** + * The type Iv generator. + */ +public class RandomIVGenerator { + /** + * Generate iv parameter spec. + * + * @return the iv parameter spec + */ + public static IvParameterSpec generateIv() { + byte[] iv = new byte[16]; // IV size for AES-128 + new SecureRandom().nextBytes(iv); + return new IvParameterSpec(iv); + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/context/UnirestContext.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/context/UnirestContext.java new file mode 100644 index 0000000..f54c8f3 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/context/UnirestContext.java @@ -0,0 +1,19 @@ +package g2pc.core.lib.security.context; + + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Getter +@Setter +public class UnirestContext { + + List roles; + + String jwtHeader; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/AsymmetricSignatureService.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/AsymmetricSignatureService.java new file mode 100644 index 0000000..f344cac --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/AsymmetricSignatureService.java @@ -0,0 +1,23 @@ +package g2pc.core.lib.security.service; + +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.cert.CertificateException; + + +@Service +public interface AsymmetricSignatureService { + + public byte[] sign(String data, InputStream fis , String password) throws InvalidKeyException, Exception; + + public PrivateKey getPrivate(InputStream fis , String password) throws Exception ; + + public PublicKey getPublic(InputStream fis , String password) throws Exception ; + + public boolean verifySignature(byte[] data, byte[] signature , InputStream fis , String password) throws Exception ; + + public KeyStore.PrivateKeyEntry extractP12Certificate(InputStream fis , String password) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableEntryException; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pEncryptDecrypt.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pEncryptDecrypt.java new file mode 100644 index 0000000..cfce5e3 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pEncryptDecrypt.java @@ -0,0 +1,16 @@ +package g2pc.core.lib.security.service; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +public interface G2pEncryptDecrypt { + + public String g2pEncrypt(String data, String key) throws Exception; + + public String g2pDecrypt(String encryptedData, String key) throws Exception; + + public String sha256Hashing(String data) throws NoSuchAlgorithmException; + + public String hmacHashing(String data , String secret) throws NoSuchAlgorithmException, InvalidKeyException; + +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pTokenService.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pTokenService.java new file mode 100644 index 0000000..7a32b83 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pTokenService.java @@ -0,0 +1,35 @@ +package g2pc.core.lib.security.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.common.security.G2pTokenResponse; +import g2pc.core.lib.dto.common.security.TokenExpiryDto; +import kong.unirest.UnirestException; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Map; + +@Service +public interface G2pTokenService { + + public G2pTokenResponse getToken(String URL ,String clientId, String clientSecret) throws IOException, UnirestException; + + + public TokenExpiryDto createTokenExpiryDto(G2pTokenResponse g2pTokenResponse); + + public Boolean isTokenExpired(TokenExpiryDto tokenExpiryDto) throws ParseException; + + public ArrayList> getClientByRealm(String masterAdminUrl, String getClientUrl , String clientId , String clientSecret + , String username , String password) throws JsonProcessingException; + + public boolean validateToken(String masterAdminUrl, String getClientUrl , String clientId , + String adminClientId , String adminClientSecret + , String username , String password) throws JsonProcessingException; + + public String decodeToken(String token) throws JsonProcessingException; + + public ResponseEntity getInterSpectResponse(String url, String token, String clientId, String clientSecret) throws UnirestException; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pcUtilityClass.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pcUtilityClass.java new file mode 100644 index 0000000..c772739 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/service/G2pcUtilityClass.java @@ -0,0 +1,16 @@ +package g2pc.core.lib.security.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.search.message.request.*; +import g2pc.core.lib.exceptions.G2pcValidationException; + +import java.util.List; +import java.util.Map; + +public interface G2pcUtilityClass { + + SearchCriteriaDTO getSearchCriteriaDTO(Map queryParamsMap, Map registrySpecificConfigMap, + List sortDTOList, RequestPaginationDTO paginationDTO, ConsentDTO consentDTO, AuthorizeDTO authorizeDTO) ; + + public void validateResponse(String inputString , String inputType) throws G2pcValidationException, JsonProcessingException; + } diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/AsymmetricSignatureServiceImpl.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/AsymmetricSignatureServiceImpl.java new file mode 100644 index 0000000..dc88ee8 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/AsymmetricSignatureServiceImpl.java @@ -0,0 +1,94 @@ +package g2pc.core.lib.security.serviceImpl; + +import g2pc.core.lib.security.service.AsymmetricSignatureService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; +import java.io.IOException; +import java.io.InputStream; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; + + +@Service +public class AsymmetricSignatureServiceImpl implements AsymmetricSignatureService { + + @Autowired + private ResourceLoader resourceLoader; + + /** + * + * @param data + * @return + * @throws InvalidKeyException + * @throws Exception + */ + @Override + public byte[] sign(String data , InputStream fis , String password) throws InvalidKeyException, Exception { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(getPrivate(fis, password)); + signature.update(data.getBytes()); + return signature.sign(); + } + + /** + * + * @return + * @throws Exception + */ + @Override + public PrivateKey getPrivate(InputStream fis , String password) throws Exception { + return extractP12Certificate(fis , password).getPrivateKey(); + } + + /** + * + * @return + * @throws Exception + */ + @Override + public PublicKey getPublic(InputStream fis , String password) throws Exception { + Certificate certificate = extractP12Certificate(fis , password).getCertificate(); + return certificate.getPublicKey(); + } + + /** + * + * @param data + * @param signature + * @return + * @throws Exception + */ + @Override + public boolean verifySignature(byte[] data, byte[] signature , InputStream fis , String password) throws Exception { + Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initVerify(getPublic(fis,password)); + sig.update(data); + + return sig.verify(signature); + } + + /** + * + * @return + * @throws IOException + * @throws KeyStoreException + * @throws CertificateException + * @throws NoSuchAlgorithmException + * @throws UnrecoverableEntryException + */ + @Override + public KeyStore.PrivateKeyEntry extractP12Certificate(InputStream fis , String password) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableEntryException { + KeyStore ks = KeyStore.getInstance("PKCS12"); + + char[] passwordInChar = password.toCharArray(); + ks.load(fis, passwordInChar); + + KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(passwordInChar); + KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("1", protectionParameter); + + return pkEntry; + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/G2pEncryptDecryptImpl.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/G2pEncryptDecryptImpl.java new file mode 100644 index 0000000..e60bb59 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/G2pEncryptDecryptImpl.java @@ -0,0 +1,119 @@ +package g2pc.core.lib.security.serviceImpl; +import g2pc.core.lib.enums.AlgorithmENUM; +import g2pc.core.lib.security.RandomIVGenerator; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import org.springframework.stereotype.Service; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + + +/** + * This class is used to return method related to digital signature and encryption + */ +@Service +public class G2pEncryptDecryptImpl implements G2pEncryptDecrypt { + + /** + * This method is used to encrypt the data in symmetric method + * @param data string to encrypt + * @param key public key + * @return + * @throws Exception + */ + @Override + public String g2pEncrypt(String data, String key) throws Exception { + IvParameterSpec ivParameterSpec = RandomIVGenerator.generateIv(); + + byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + SecretKey secretKey = new SecretKeySpec(keyBytes, AlgorithmENUM.AES.toValue()); + + Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec); + byte[] encryptedData = aesCipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); + + String ivStr = Base64.getEncoder().encodeToString(ivParameterSpec.getIV()); + String encryptedDataStr = Base64.getEncoder().encodeToString(encryptedData); + + return ivStr + ":" + encryptedDataStr; + } + + /** + * This method is used to decrypt the encrypted string in symmetric method + * @param encryptedData string to decrypt + * @param key public key + * @return + * @throws Exception + */ + @Override + public String g2pDecrypt(String encryptedData, String key) throws Exception { + String[] parts = encryptedData.split(":"); + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid encrypted data format"); + } + + String ivStr = parts[0]; + String encryptedDataStr = parts[1]; + + byte[] ivBytes = Base64.getDecoder().decode(ivStr); + byte[] encryptedDataBytes = Base64.getDecoder().decode(encryptedDataStr); + + byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); + SecretKey secretKey = new SecretKeySpec(keyBytes, AlgorithmENUM.AES.toValue()); + + Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + aesCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(ivBytes)); + byte[] decryptedDataBytes = aesCipher.doFinal(encryptedDataBytes); + + return new String(decryptedDataBytes, StandardCharsets.UTF_8); + } + + /** + * Used to apply hashing algorithm to signature + * @param data data to get hashed + * @return + * @throws NoSuchAlgorithmException + */ + @Override + public String sha256Hashing(String data) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance( AlgorithmENUM.SHA256.toValue() ) ; + byte[ ] hash = md.digest( data.getBytes( StandardCharsets.UTF_8 ) ) ; + BigInteger number = new BigInteger( 1, hash ) ; + StringBuilder hexString = new StringBuilder( number.toString( 16 ) ) ; + while ( hexString.length( ) < 32 ) + { + hexString.insert( 0, " 0 " ) ; + } + return hexString.toString( ) ; + } + + /** + * Used to apply Hmac hashing algorithm to signature + * @param data data to get hashed + * @param secret secret key + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + @Override + public String hmacHashing(String data , String secret) throws NoSuchAlgorithmException, InvalidKeyException { + Mac sha256HMAC = Mac.getInstance(AlgorithmENUM.HMAC.toValue()); + SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + sha256HMAC.init(secretKey); + + byte[] hashByte = sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8)); + String hash = Base64.getEncoder().encodeToString(hashByte); + + return hash; + } + +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/G2pTokenServiceImpl.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/G2pTokenServiceImpl.java new file mode 100644 index 0000000..c03a9ee --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/G2pTokenServiceImpl.java @@ -0,0 +1,215 @@ +package g2pc.core.lib.security.serviceImpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.config.G2pUnirestHelper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.security.G2pTokenResponse; +import g2pc.core.lib.dto.common.security.TokenExpiryDto; +import g2pc.core.lib.security.service.G2pTokenService; +import kong.unirest.HttpResponse; +import kong.unirest.JsonNode; +import kong.unirest.Unirest; +import kong.unirest.UnirestException; +import kong.unirest.json.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import java.io.IOException; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + + +/** + * Service contains methods related to security using token + */ +@Service +@Slf4j +public class G2pTokenServiceImpl implements G2pTokenService { + + + @Autowired + G2pUnirestHelper g2pUnirestHelper; + + public static String CLIENT_ID= "clientId"; + + /** + * Method to generate token + * @param URL keycloak url + * @param clientId clientID + * @param clientSecret clientSecret + * @return G2pTokenResponse + * @throws IOException might be thrown + * @throws UnirestException might be thrown + */ + @Override + public G2pTokenResponse getToken(String URL, String clientId, String clientSecret) throws IOException, UnirestException { + + String grantType = "client_credentials"; + ObjectMapper objectMapper = new ObjectMapper(); + // Make an HTTP POST request using Unirest + HttpResponse response = Unirest.post(URL) + .header(CoreConstants.CONTENT_TYPE, "application/x-www-form-urlencoded") + .field(CoreConstants.GRANT_TYPE, grantType) + .field(CoreConstants.CLIENT_ID, clientId) + .field(CoreConstants.CLIENT_SECRET, clientSecret) + .asJson(); + + Map body = objectMapper.readValue(response.getBody().toString(), new TypeReference>() { + }); + G2pTokenResponse tokenResponse = new G2pTokenResponse(); + tokenResponse.setAccessToken(body.get(CoreConstants.ACCESS_TOKEN).toString()); + tokenResponse.setTokenType(body.get(CoreConstants.TOKEN_TYPE).toString()); + tokenResponse.setExpiresIn(body.get(CoreConstants.EXPIRES_IN).toString()); + return tokenResponse; + } + + /** + * Method to create tokenExpiryDto + * @param g2pTokenResponse to create token expiry dto + * @return tokenExpiryDto + */ + @Override + public TokenExpiryDto createTokenExpiryDto(G2pTokenResponse g2pTokenResponse) { + TokenExpiryDto tokenExpiryDto = new TokenExpiryDto(); + tokenExpiryDto.setToken(g2pTokenResponse.getAccessToken()); + tokenExpiryDto.setExpiresIn(g2pTokenResponse.getExpiresIn()); + Timestamp currentTimeStamp = new Timestamp(System.currentTimeMillis()); + tokenExpiryDto.setDateSaved(currentTimeStamp); + return tokenExpiryDto; + } + + /** + * Method to check whether token is expired or not by calculations + * @param tokenExpiryDto to check token expired + * @return result + * @throws ParseException might be thrown + */ + @Override + public Boolean isTokenExpired(TokenExpiryDto tokenExpiryDto) throws ParseException { + if (tokenExpiryDto != null) { + String lastSaved = tokenExpiryDto.getDateSaved().toString(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); + Date parsedDate = sdf.parse(lastSaved); + Timestamp lastTimeStamp = new Timestamp(parsedDate.getTime()); + Timestamp currentTimeStamp = new Timestamp(System.currentTimeMillis()); + long milliseconds = currentTimeStamp.getTime() - lastTimeStamp.getTime(); + int seconds = (int) milliseconds / 1000; + int minutes = seconds / 60; + int expiry = Integer.parseInt(tokenExpiryDto.getExpiresIn()) / 60; + return minutes > expiry; + + } + return true; + } + + /** + * Method to return clients present in realm of keycloak to validate token + * @param masterAdminUrl master admin url + * @param getClientUrl get client url + * @return clients + * @throws JsonProcessingException might be thrown + */ + @Override + public ArrayList> getClientByRealm(String masterAdminUrl, String getClientUrl , String adminClientId , String adminClientSecret + , String username , String password) throws JsonProcessingException { + String grantType = "password"; + ObjectMapper objectMapper = new ObjectMapper(); + ArrayList> responseMap = new ArrayList<>(); + HttpResponse response = Unirest.post(masterAdminUrl) + .header(CoreConstants.CONTENT_TYPE, "application/x-www-form-urlencoded") + .field(CoreConstants.GRANT_TYPE, grantType) + .field(CoreConstants.CLIENT_ID, adminClientId) + .field(CoreConstants.CLIENT_SECRET, adminClientSecret) + .field( CoreConstants.USERNAME,username) + .field(CoreConstants.PASSWORD,password) + .asJson(); + Map responseBody = objectMapper.readValue(response.getBody().toString(), new TypeReference>() {}); + if(responseBody.get(CoreConstants.ACCESS_TOKEN)!=null){ + String token = responseBody.get(CoreConstants.ACCESS_TOKEN).toString(); + HttpResponse clientResponse = g2pUnirestHelper.g2pGet(getClientUrl) + .header(CoreConstants.CONTENT_TYPE, "application/json") + .header(CoreConstants.AUTHORIZATION, "Bearer " + token) + .asString(); + responseMap = objectMapper.readValue(clientResponse.getBody(), ArrayList.class); + } + return responseMap; + + } + + /** + * Method to validate the token whether its present in client list of respective realm + * @param masterAdminUrl keycloak master url + * @param getClientUrl keycloak client url + * @param clientId client id for validate + * @return result + * @throws JsonProcessingException might be thrown + */ + @Override + public boolean validateToken(String masterAdminUrl, String getClientUrl , String clientId , + String adminClientId , String adminClientSecret + , String username , String password) throws JsonProcessingException { + ArrayList> responseMap = getClientByRealm(masterAdminUrl, getClientUrl , adminClientId , adminClientSecret , username , password); + boolean isValid = false; + for (int i = 0; i < responseMap.size(); i++) { + String responseClientId = responseMap.get(i).get("clientId"); + isValid = responseClientId.equals(clientId); + if (isValid) { + return true; + } + } + return false; + } + + /** + * Method to decode the token + * @param jwtToken token to decode + * @return clientId + * @throws JsonProcessingException might be thrown + */ + @Override + public String decodeToken(String jwtToken) throws JsonProcessingException { + + ObjectMapper objectMapper = new ObjectMapper(); + String[] splitString = jwtToken.split("\\."); + String base64EncodedPayload = splitString[1]; + org.apache.commons.codec.binary.Base64 base64Url = new Base64(true); + String body = new String(base64Url.decode(base64EncodedPayload)); + HashMap payLoad = objectMapper.readValue(body, HashMap.class); + return payLoad.get(CLIENT_ID); + } + + /** + * Method to do introspect of token using keycloak api + * @param url url for keycloak + * @param token token to verify + * @param clientId clientId to verify + * @param clientSecret clientSecret to verify + * @return result + * @throws UnirestException might be thrown + */ + @Override + public ResponseEntity getInterSpectResponse(String url, String token, String clientId, String clientSecret) throws UnirestException { + HttpResponse introResponse = Unirest.post(url) + .header(CoreConstants.CONTENT_TYPE, "application/x-www-form-urlencoded") + .field("token", token) + .field(CoreConstants.CLIENT_ID, clientId) + .field(CoreConstants.CLIENT_SECRET, clientSecret) + .asString(); + + JSONObject json = new JSONObject(introResponse.getBody()); + if(json!=null && !json.getString("active").isEmpty()) { + String isValid = json.getString("active"); + if (isValid.equals("true")) { + return ResponseEntity.status(HttpStatus.OK).body("Token is valid"); + } + } + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Token is not valid"); + } +} \ No newline at end of file diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/UtlitiyImpl.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/UtlitiyImpl.java new file mode 100644 index 0000000..9dd7a25 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/security/serviceImpl/UtlitiyImpl.java @@ -0,0 +1,103 @@ +package g2pc.core.lib.security.serviceImpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.JsonSchema; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.search.message.request.*; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.core.lib.security.service.G2pcUtilityClass; +import g2pc.core.lib.utils.CommonUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +@Service +public class UtlitiyImpl implements G2pcUtilityClass { + + + @Autowired + private CommonUtils commonUtils; + @Override + public SearchCriteriaDTO getSearchCriteriaDTO(Map queryParamsMap, Map registrySpecificConfigMap, List sortDTOList, RequestPaginationDTO paginationDTO, ConsentDTO consentDTO, AuthorizeDTO authorizeDTO) { + SearchCriteriaDTO searchCriteriaDTO = new SearchCriteriaDTO(); + searchCriteriaDTO.setVersion("1.0.0"); + searchCriteriaDTO.setRegType(registrySpecificConfigMap.get("reg_type").toString()); + searchCriteriaDTO.setRegSubType(registrySpecificConfigMap.get("reg_sub_type").toString()); + searchCriteriaDTO.setQueryType(registrySpecificConfigMap.get("query_type").toString()); + + QueryDTO queryDTO = new QueryDTO(); + queryDTO.setQueryName(registrySpecificConfigMap.get("query_name").toString()); + queryDTO.setQueryParams(queryParamsMap.values().iterator().hasNext() ? queryParamsMap.values().iterator().next() : ""); + + searchCriteriaDTO.setQuery(queryDTO); + searchCriteriaDTO.setSort(sortDTOList); + searchCriteriaDTO.setPagination(paginationDTO); + searchCriteriaDTO.setConsent(consentDTO); + searchCriteriaDTO.setAuthorize(authorizeDTO); + return searchCriteriaDTO; + } + + /** + * + * @param inputString inputString to validate + * @param inputType type of input + * @throws G2pcValidationException might be thrown + * @throws JsonProcessingException might be thrown + */ + @Override + public void validateResponse(String inputString, String inputType) throws G2pcValidationException, JsonProcessingException { + + ObjectMapper objectMapper = new ObjectMapper(); + InputStream schemaStream = getJsonInputStream(inputType); + JsonNode jsonNodeMessage = objectMapper.readTree(inputString); + JsonSchema schemaMessage = null; + if(schemaStream !=null){ + schemaMessage = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4). + getSchema(schemaStream); + } + Set errorMessage = schemaMessage.validate(jsonNodeMessage); + List errorCombinedMessage= new ArrayList<>(); + for (ValidationMessage error : errorMessage){ + log.info("Validation errors" + error ); + errorCombinedMessage.add(new G2pcError("",error.getMessage())); + } + if (!errorMessage.isEmpty()){ + throw new G2pcValidationException(errorCombinedMessage); + } + } + + /** + * + * @param inputType type of input stream + * @return + */ + private InputStream getJsonInputStream(String inputType) { + if(inputType.equals(CoreConstants.RESPONSE_HEADER)){ + return commonUtils.getResponseHeaderString(); + } else if (inputType.equals(CoreConstants.SEARCH_RESPONSE)){ + return commonUtils.getResponseMessageString(); + } else if (inputType.equals(CoreConstants.STATUS_RESPONSE)){ + return commonUtils.getStatusResponseMessageString(); + } + else if(inputType.equals(CoreConstants.REQUEST_HEADER)){ + return commonUtils.getRequestHeaderString(); + } else if (inputType.equals(CoreConstants.SEARCH_REQUEST)){ + return commonUtils.getRequestMessageString(); + } else { + return commonUtils.getStatusRequestMessageString(); + } + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/service/ElasticsearchService.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/service/ElasticsearchService.java new file mode 100644 index 0000000..ad4f87d --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/service/ElasticsearchService.java @@ -0,0 +1,13 @@ +package g2pc.core.lib.service; + +import org.elasticsearch.action.search.SearchResponse; + +import java.io.IOException; +import java.util.Map; + +public interface ElasticsearchService { + + SearchResponse exactSearch(String index, Map fieldValues) throws IOException; + + void clearData(String index) throws IOException; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/service/SftpHandlerService.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/service/SftpHandlerService.java new file mode 100644 index 0000000..29ad7e6 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/service/SftpHandlerService.java @@ -0,0 +1,20 @@ +package g2pc.core.lib.service; + +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.SftpException; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import org.springframework.integration.core.MessageSource; +import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizer; +import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizingMessageSource; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import org.springframework.messaging.MessageChannel; + +import java.io.File; +import java.io.IOException; + +public interface SftpHandlerService { + + Boolean uploadFileToSftp(SftpServerConfigDTO serverConfigDTO, String localFilePath, String remoteDirectory); + + ChannelSftp getJschSession(SftpServerConfigDTO serverConfigDTO) throws IOException; +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/serviceimpl/ElasticsearchServiceImpl.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/serviceimpl/ElasticsearchServiceImpl.java new file mode 100644 index 0000000..adc1766 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/serviceimpl/ElasticsearchServiceImpl.java @@ -0,0 +1,52 @@ +package g2pc.core.lib.serviceimpl; + +import g2pc.core.lib.service.ElasticsearchService; +import lombok.extern.slf4j.Slf4j; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; + +import org.elasticsearch.index.query.BoolQueryBuilder; + +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.reindex.BulkByScrollResponse; +import org.elasticsearch.index.reindex.DeleteByQueryRequest; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Map; + + +@Service +@Slf4j +public class ElasticsearchServiceImpl implements ElasticsearchService { + + + @Autowired + private RestHighLevelClient client; + + @Override + public SearchResponse exactSearch(String index, Map fieldValues) throws IOException { + SearchRequest searchRequest = new SearchRequest(index); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); + for (Map.Entry entry : fieldValues.entrySet()) { + boolQueryBuilder.must(QueryBuilders.termQuery(entry.getKey(), entry.getValue())); + } + searchSourceBuilder.query(boolQueryBuilder); + searchRequest.source(searchSourceBuilder); + log.info("searchRequest: {}", searchSourceBuilder); + return client.search(searchRequest, RequestOptions.DEFAULT); + } + + @Override + public void clearData(String index) throws IOException { + DeleteByQueryRequest request = new DeleteByQueryRequest(index); + request.setQuery(QueryBuilders.matchAllQuery()); + BulkByScrollResponse deleteResponse = client.deleteByQuery(request, RequestOptions.DEFAULT); + log.info("DeleteResponse: {}", deleteResponse.toString()); + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/serviceimpl/SftpHandlerServiceImpl.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/serviceimpl/SftpHandlerServiceImpl.java new file mode 100644 index 0000000..95de036 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/serviceimpl/SftpHandlerServiceImpl.java @@ -0,0 +1,62 @@ +package g2pc.core.lib.serviceimpl; + +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpException; +import g2pc.core.lib.constants.SftpConstants; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.service.SftpHandlerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +@Slf4j +public class SftpHandlerServiceImpl implements SftpHandlerService { + + @Override + public Boolean uploadFileToSftp(SftpServerConfigDTO serverConfigDTO, String localFilePath, + String remoteDirectory) { + ChannelSftp sftpChannel; + try { + sftpChannel = getJschSession(serverConfigDTO); + } catch (IOException e) { + return false; + } + try { + sftpChannel.put(localFilePath, remoteDirectory); + return true; + } catch (SftpException e) { + log.error(SftpConstants.UPLOAD_ERROR_MESSAGE, e); + return false; + } finally { + sftpChannel.exit(); + } + } + + @Override + public ChannelSftp getJschSession(SftpServerConfigDTO serverConfigDTO) throws IOException { + JSch jsch = new JSch(); + Session session = null; + ChannelSftp sftpChannel; + try { + session = jsch.getSession(serverConfigDTO.getUser(), + serverConfigDTO.getHost(), + serverConfigDTO.getPort()); + session.setPassword(serverConfigDTO.getPassword()); + session.setConfig(SftpConstants.STRICT_HOST_KEY_CHECKING, serverConfigDTO.getStrictHostKeyChecking()); + session.connect(); + sftpChannel = (ChannelSftp) session.openChannel(serverConfigDTO.getSessionChannel()); + sftpChannel.connect(); + return sftpChannel; + } catch (Exception e) { + log.error(SftpConstants.UPLOAD_ERROR_MESSAGE, e); + if (session != null) { + session.disconnect(); + } + throw new IOException(e); + } + } +} diff --git a/g2pc-core-lib/src/main/java/g2pc/core/lib/utils/CommonUtils.java b/g2pc-core-lib/src/main/java/g2pc/core/lib/utils/CommonUtils.java new file mode 100644 index 0000000..5909a06 --- /dev/null +++ b/g2pc-core-lib/src/main/java/g2pc/core/lib/utils/CommonUtils.java @@ -0,0 +1,114 @@ +package g2pc.core.lib.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +/** + * The type Common utils. + */ +@Service +@Slf4j +public class CommonUtils { + + /** + * Gets current time stamp. + * + * @return the current time stamp + */ + public static String getCurrentTimeStamp() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + Date date = new Date(); + return dateFormat.format(date); + } + + + /** + * Get request header string input stream. + * + * @return the input stream + */ + public InputStream getRequestHeaderString() { + return CommonUtils.class.getClassLoader() + .getResourceAsStream("schema/HeaderSchema.json"); + } + + /** + * Get request message string input stream. + * + * @return the input stream + */ + public InputStream getRequestMessageString() { + return CommonUtils.class.getClassLoader() + .getResourceAsStream("schema/MessageSchema.json"); + } + + /** + * Get response header string input stream. + * + * @return the input stream + */ + public InputStream getResponseHeaderString() { + return CommonUtils.class.getClassLoader() + .getResourceAsStream("schema/ResponseHeaderSchema.json"); + } + + /** + * Get response message string input stream. + * + * @return the input stream + */ + public InputStream getResponseMessageString() { + return CommonUtils.class.getClassLoader() + .getResourceAsStream("schema/ResponseMessageSchema.json"); + } + + /** + * Get status request message string input stream. + * @return + */ + public InputStream getStatusRequestMessageString(){ + return CommonUtils.class.getClassLoader() + .getResourceAsStream("schema/StatusRequestMessageSchema.json"); + } + + /** + * Get status response message string input stream. + * @return + */ + public InputStream getStatusResponseMessageString(){ + return CommonUtils.class.getClassLoader() + .getResourceAsStream("schema/StatusResponseMessageSchema.json"); + } + + /** + * Generate unique ID + * + * @param idType whether transactionId, correlationId or referenceId + * @return unique ID + */ + public static String generateUniqueId(String idType) { + String uniqueNumString = Long.toString(UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE); + return idType + uniqueNumString.substring(0, 3) + + "-" + uniqueNumString.substring(3, 7) + + "-" + uniqueNumString.substring(7, 11) + + "-" + uniqueNumString.substring(11, 15) + + "-" + uniqueNumString.substring(15); + } + + /** + * Format a string + * + * @param data required + * @return formatted string + */ + public static String formatString(String data) { + return data.replace("\\", ""). + replace("\"{", "{"). + replace("}\"", "}"); + } +} diff --git a/g2pc-core-lib/src/main/resources/private.p12 b/g2pc-core-lib/src/main/resources/private.p12 new file mode 100644 index 0000000..c067bff Binary files /dev/null and b/g2pc-core-lib/src/main/resources/private.p12 differ diff --git a/g2pc-core-lib/src/main/resources/schema/HeaderSchema.json b/g2pc-core-lib/src/main/resources/schema/HeaderSchema.json new file mode 100644 index 0000000..188edfb --- /dev/null +++ b/g2pc-core-lib/src/main/resources/schema/HeaderSchema.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "header schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "version" : { + "type": [ "string", "null" ] + }, + "message_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "message_ts": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "action": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "sender_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "sender_uri": { + "type": ["string","null"] + }, + "receiver_id": { + "type": ["string","null"] + }, + "total_count": { + "type": "number" + }, + "is_msg_encrypted": { + "type": ["boolean","null"], + "default": "false" + }, + "meta": { + "type": [ "object", "null" ] + } + }, + "required": ["message_id","message_ts","action","sender_id","total_count"], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} diff --git a/g2pc-core-lib/src/main/resources/schema/MessageSchema.json b/g2pc-core-lib/src/main/resources/schema/MessageSchema.json new file mode 100644 index 0000000..378085b --- /dev/null +++ b/g2pc-core-lib/src/main/resources/schema/MessageSchema.json @@ -0,0 +1,114 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Message schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties" : { + "type": { + "type": "string" + }, + "transaction_id": { + "type" : "string", + "minLength": 0, + "maxLength": 99, + "$ref": "#/definitions/nonEmptyString" + }, + "search_request" :{ + "type": "array", + "items" : [{ + + "type" : "object", + "properties" : { + "reference_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "timestamp": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "search_criteria" : { + "type": "object" , + "properties": { + "version": { + "type": "string" + }, + "reg_type": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "reg_sub_type" : { + "type": "string" + }, + "query_type": { + "type": "string", + "$ref": "#/definitions/nonEmptyString", + "enum": ["namedQuery","idtype","predicate"] + }, + + "sort" : { + "type": "array", + "items": [ + { + "type": "object" , + "properties": { + "attribute_name" : { + "type": "string" + }, + "sort_order" : { + "type": "string", + "enum": ["asc" , "desc"] + } + } + } + ] + }, + "pagination" : { + "type": "object", + "properties": { + "page_size": { + "type": "number" + }, + "page_number": { + "type": "number" + } + }, + "required": ["page_size"] + }, + "consent": { + "type": "object" + }, + "authorize": { + "type": "object" + } + }, + "required": [ + "reg_type" ,"query_type" + ] + }, + "locale" : { + "type": "string" + + } + }, + "required": [ + "reference_id" ,"timestamp" , "search_criteria" + ] + + + }] + } + } , + "required": [ + "transaction_id" , "search_request" + ], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} + diff --git a/g2pc-core-lib/src/main/resources/schema/ResponseHeaderSchema.json b/g2pc-core-lib/src/main/resources/schema/ResponseHeaderSchema.json new file mode 100644 index 0000000..2582f72 --- /dev/null +++ b/g2pc-core-lib/src/main/resources/schema/ResponseHeaderSchema.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "header response schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "version" : { + "type": "string" + }, + "message_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "message_ts": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "action": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "status" : { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "status_reason_code" : { + "type" : [ "string", "null" ], + "enum": ["rjct.reference_id.invalid", "rjct.reference_id.duplicate", + "rjct.timestamp.invalid" ,"rjct.search_criteria.invalid" , + "rjct.filter.invalid" ,"rjct.sort.invalid" , + "rjct.pagination.invalid" ,"rjct.search.too_many_records_found","succ"] + }, + "status_reason_message": { + "anyOf": [ + { "type": "string" }, + { "type": "null" } + ], + "minLength": 0, + "maxLength": 99 + }, + "total_count": { + "type": "number" + }, + "completed_count": { + "type": "number" + }, + "sender_id": { + "type": "string" + }, + "receiver_id": { + "type": "string" + }, + "is_msg_encrypted": { + "type": "boolean" + }, + "meta": { + "type": [ "object", "null" ] + } + }, + "required": ["message_id","message_ts","action","total_count"], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} diff --git a/g2pc-core-lib/src/main/resources/schema/ResponseMessageSchema.json b/g2pc-core-lib/src/main/resources/schema/ResponseMessageSchema.json new file mode 100644 index 0000000..aeaa70f --- /dev/null +++ b/g2pc-core-lib/src/main/resources/schema/ResponseMessageSchema.json @@ -0,0 +1,104 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Message schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties" : { + "type": { + "type": "string" + }, + "transaction_id": { + "type" : "string", + "minLength": 0, + "maxLength": 99, + "$ref": "#/definitions/nonEmptyString" + }, + "correlation_id": { + "type" : "string", + "minLength": 0, + "maxLength": 99, + "$ref": "#/definitions/nonEmptyString" + }, + "search_response" : { + "type": "array", + "items": [{ + "type" : "object", + "properties" : { + "reference_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "timestamp": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "status": { + "type": "string", + "enum": ["rcvd","pdng","succ","rjct"], + "$ref": "#/definitions/nonEmptyString" + }, + "status_reason_code" : { + "type": "string", + "enum": ["rjct.reference_id.invalid", "rjct.reference_id.duplicate", + "rjct.timestamp.invalid" ,"rjct.search_criteria.invalid" , + "rjct.filter.invalid" ,"rjct.sort.invalid" , + "rjct.pagination.invalid" ,"rjct.search.too_many_records_found" ,"succ","record_not_found"] + }, + "status_reason_message": { + "type": "string", + "minLength": 0, + "maxLength": 99 + }, + "data" : { + "type": "object", + "properties": { + "version" : { + "type": "string" + }, + "reg_type": { + "type": "string" + }, + "reg_sub_type": { + "type": "string" + }, + "reg_record_type": { + "type": "string" + } + }, + "required": ["reg_type","reg_record_type"] + }, + "pagination" : { + "type": "object", + "properties": { + "page_size": { + "type": "number" + }, + "page_number": { + "type": "number" + } + }, + "required": ["page_size"] + }, + "locale" : { + "type": "string" + } + }, + "required": [ + "reference_id" ,"timestamp" , "status" + ] + }] + } + } , + "required": [ + "transaction_id","correlation_id" , "search_response" + ], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} + diff --git a/g2pc-core-lib/src/main/resources/schema/StatusRequestMessageSchema.json b/g2pc-core-lib/src/main/resources/schema/StatusRequestMessageSchema.json new file mode 100644 index 0000000..5cd76fe --- /dev/null +++ b/g2pc-core-lib/src/main/resources/schema/StatusRequestMessageSchema.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Message schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties" : { + "type": { + "type": "string" + }, + "transaction_id": { + "type" : "string", + "minLength": 0, + "maxLength": 99, + "$ref": "#/definitions/nonEmptyString" + }, + "txnstatus_request" :{ + "type" : "object", + "properties" : { + "reference_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "txn_type" : { + "type" : [ "string", "null" ], + "enum": ["search","subscribe","unsubscribe"], + "$ref": "#/definitions/nonEmptyString" + }, + "attribute_type" : { + "type" : [ "string", "null" ], + "enum": ["transaction_id","reference_id_list","correlation_id","subscription_code_list"], + "$ref": "#/definitions/nonEmptyString" + }, + "attribute_value" : { + "type": ["object","string"], + "$ref": "#/definitions/nonEmptyString" + } + } + } + } , + "required": [ + "transaction_id" , "txnstatus_request" + ], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} + diff --git a/g2pc-core-lib/src/main/resources/schema/StatusResponseMessageSchema.json b/g2pc-core-lib/src/main/resources/schema/StatusResponseMessageSchema.json new file mode 100644 index 0000000..0b9b193 --- /dev/null +++ b/g2pc-core-lib/src/main/resources/schema/StatusResponseMessageSchema.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Message schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties" : { + "type": { + "type": "string" + }, + "transaction_id": { + "type" : "string", + "minLength": 0, + "maxLength": 99, + "$ref": "#/definitions/nonEmptyString" + }, + "correlation_id": { + "type" : "string", + "minLength": 0, + "maxLength": 99, + "$ref": "#/definitions/nonEmptyString" + }, + "txnstatus_response" : { + "type" : "object", + "properties" : { + "txn_type": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "txn_status": { + "type": "object" + } + } + } + + + }, + + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} + + + diff --git a/g2pc-dc-core-lib/.gitignore b/g2pc-dc-core-lib/.gitignore new file mode 100644 index 0000000..e23d88f --- /dev/null +++ b/g2pc-dc-core-lib/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +mvnw +mvnw.cmd +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +.idea/ + diff --git a/g2pc-dc-core-lib/README.md b/g2pc-dc-core-lib/README.md new file mode 100644 index 0000000..1c5fa29 --- /dev/null +++ b/g2pc-dc-core-lib/README.md @@ -0,0 +1,25 @@ +# G2pc DC Core Lib + +## Overview +### Json Schema validations +* In this project Json schema input stream return by parent g2p-core lib. +* In ResponseHandlerServiceImpl class input stream is called and will validate the Response DTO header and message +* If any thing doesn't match with the json schema exception handling is written for same. +* Below are some reference code for same. + +```` + InputStream schemaStream = commonUtils.getRequestMessageString(); + JsonNode jsonNodeMessage = objectMapper.readTree(messageString); + JsonSchema schemaMessage = null; + if(schemaStream !=null){ + schemaMessage = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4). + getSchema(schemaStream); + } + Set errorMessage = schemaMessage.validate(jsonNodeMessage); + List errorcombinedMessage= new ArrayList<>(); + for (ValidationMessage error : errorMessage){ + log.info("Validation errors" + error ); + errorcombinedMessage.add(new G2pcError("",error.getMessage())); + + } +```` \ No newline at end of file diff --git a/g2pc-dc-core-lib/pom.xml b/g2pc-dc-core-lib/pom.xml new file mode 100644 index 0000000..3ddc62b --- /dev/null +++ b/g2pc-dc-core-lib/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.0.12 + + + g2pc.dc.core.lib + g2pc-dc-core-library + 0.0.1-SNAPSHOT + g2pc-dc-core-library + g2pc-dc-core-library + + 17 + + + + org.springframework.boot + spring-boot-starter + + + org.yaml + snakeyaml + 2.2 + + + org.projectlombok + lombok + true + + + g2pc.core.lib + g2pc-core-library + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.postgresql + postgresql + 42.5.4 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + 7.10.2 + + + diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/G2pcDcCoreLibraryApplication.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/G2pcDcCoreLibraryApplication.java new file mode 100644 index 0000000..789116d --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/G2pcDcCoreLibraryApplication.java @@ -0,0 +1,15 @@ +package g2pc.dc.core.lib; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan({"g2pc.dc.core.lib","g2pc.core.lib","g2pc.core.lib.security.service"}) +public class G2pcDcCoreLibraryApplication { + + public static void main(String[] args) { + SpringApplication.run(G2pcDcCoreLibraryApplication.class, args); + } + +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/constants/DcConstants.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/constants/DcConstants.java new file mode 100644 index 0000000..e2fe4e9 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/constants/DcConstants.java @@ -0,0 +1,15 @@ +package g2pc.dc.core.lib.constants; + +public class DcConstants { + + private DcConstants() { + } + public static final String COMPLETED = "COMPLETED"; + + + public static final String DB_TYPE_POSTGRES = "postgres"; + + public static final String DB_TYPE_REGISTRY = "registry"; + + public static final String OSID = "osid"; +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/dto/ResponseDataDto.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/dto/ResponseDataDto.java new file mode 100644 index 0000000..195d5d2 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/dto/ResponseDataDto.java @@ -0,0 +1,80 @@ +package g2pc.dc.core.lib.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import g2pc.core.lib.utils.CommonUtils; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class ResponseDataDto { + + public ResponseDataDto() { + this.registryTransactionsId = ""; + this.referenceId = ""; + this.timestamp = ""; + this.status = ""; + this.statusReasonCode = ""; + this.statusReasonMessage = ""; + this.version = ""; + this.regType = ""; + this.regSubType = ""; + this.regRecordType = ""; + this.regRecords = ""; + this.txnType = ""; + this.attributeType = ""; + this.attributeValue = ""; + this.createdDate = CommonUtils.getCurrentTimeStamp(); + this.lastUpdatedDate = CommonUtils.getCurrentTimeStamp(); + } + + @JsonProperty("registry_transactions_id") + private String registryTransactionsId; + + @JsonProperty("reference_id") + private String referenceId; + + @JsonProperty("timestamp") + private String timestamp; + + @JsonProperty("status") + private String status; + + @JsonProperty("status_reason_code") + private String statusReasonCode; + + @JsonProperty("status_reason_message") + private String statusReasonMessage; + + @JsonProperty("version") + private String version; + + @JsonProperty("reg_type") + private String regType; + + @JsonProperty("reg_sub_type") + private String regSubType; + + @JsonProperty("reg_record_type") + private String regRecordType; + + @JsonProperty("reg_records") + private String regRecords; + + @JsonProperty("txn_type") + private String txnType; + + @JsonProperty("attribute_type") + private String attributeType; + + @JsonProperty("attribute_value") + private String attributeValue; + + @JsonProperty("created_date") + private String createdDate; + + @JsonProperty("last_updated_date") + private String lastUpdatedDate; +} \ No newline at end of file diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/dto/ResponseTrackerDto.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/dto/ResponseTrackerDto.java new file mode 100644 index 0000000..a235144 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/dto/ResponseTrackerDto.java @@ -0,0 +1,103 @@ +package g2pc.dc.core.lib.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import g2pc.core.lib.utils.CommonUtils; +import lombok.*; + +@Data +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) + +public class ResponseTrackerDto { + public ResponseTrackerDto() { + this.version = ""; + this.messageId = ""; + this.messageTs = ""; + this.action = ""; + this.status = ""; + this.statusReasonCode = ""; + this.statusReasonMessage = ""; + this.totalCount = 0; + this.completedCount = 0; + this.senderId = ""; + this.receiverId = ""; + this.isMsgEncrypted = false; + this.meta = ""; + this.transactionId = ""; + this.correlationId = ""; + this.registryType = ""; + this.protocol = ""; + this.payloadFilename = ""; + this.inboundFilename = ""; + this.outboundFilename = ""; + this.createdDate = CommonUtils.getCurrentTimeStamp(); + this.lastUpdatedDate = CommonUtils.getCurrentTimeStamp(); + } + + @JsonProperty( "version") + private String version; + + @ JsonProperty( "message_id") + private String messageId; + + @ JsonProperty( "message_ts") + private String messageTs; + + @ JsonProperty( "action") + private String action; + + @ JsonProperty( "status") + private String status; + + @ JsonProperty( "status_reason_code") + private String statusReasonCode; + + @ JsonProperty( "status_reason_message") + private String statusReasonMessage; + + @ JsonProperty( "total_count") + private Integer totalCount; + + @ JsonProperty( "completed_count") + private Integer completedCount; + + @ JsonProperty( "sender_id") + private String senderId; + + @ JsonProperty( "receiver_id") + private String receiverId; + + @ JsonProperty( "is_msg_encrypted") + private Boolean isMsgEncrypted; + + @ JsonProperty( "meta") + private String meta; + + @ JsonProperty( "transaction_id") + private String transactionId; + + @ JsonProperty( "correlation_id") + private String correlationId; + + @ JsonProperty( "registry_type") + private String registryType; + + @ JsonProperty( "protocol") + private String protocol; + + @ JsonProperty( "payload_filename") + private String payloadFilename; + + @ JsonProperty( "inbound_filename") + private String inboundFilename; + + @ JsonProperty( "outbound_filename") + private String outboundFilename; + + @JsonProperty("created_date") + private String createdDate; + + @JsonProperty("last_updated_date") + private String lastUpdatedDate; +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/entity/ResponseDataEntity.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/entity/ResponseDataEntity.java new file mode 100644 index 0000000..1c453ab --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/entity/ResponseDataEntity.java @@ -0,0 +1,70 @@ +package g2pc.dc.core.lib.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.sql.Timestamp; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "response_data", schema = "g2pc") +public class ResponseDataEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "reference_id") + private String referenceId; + + @Column(name = "timestamp") + private String timestamp; + + @Column(name = "status") + private String status; + + @Column(name = "status_reason_code") + private String statusReasonCode; + + @Column(name = "status_reason_message") + private String statusReasonMessage; + + @Column(name = "version") + private String version; + + @Column(name = "reg_type") + private String regType; + + @Column(name = "reg_sub_type") + private String regSubType; + + @Column(name = "reg_record_type") + private String regRecordType; + + @Column(name = "reg_records") + private String regRecords; + + @Column(name = "txn_type") + private String txnType; + + @Column(name = "attribute_type") + private String attributeType; + + @Column(name = "attribute_value") + private String attributeValue; + + @Column(insertable = false, updatable = false) + private Timestamp createdDate; + + @Column(insertable = false) + private Timestamp lastUpdatedDate; + + @ManyToOne(targetEntity = ResponseTrackerEntity.class, cascade = CascadeType.ALL) + @JoinColumn(name = "registry_transactions_id", referencedColumnName = "id") + private ResponseTrackerEntity responseTrackerEntity; +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/entity/ResponseTrackerEntity.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/entity/ResponseTrackerEntity.java new file mode 100644 index 0000000..5ac2021 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/entity/ResponseTrackerEntity.java @@ -0,0 +1,90 @@ +package g2pc.dc.core.lib.entity; + +import jakarta.persistence.*; +import lombok.*; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "response_tracker", schema = "g2pc") +public class ResponseTrackerEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "version") + private String version; + + @Column(name = "message_id") + private String messageId; + + @Column(name = "message_ts") + private String messageTs; + + @Column(name = "action") + private String action; + + @Column(name = "status") + private String status; + + @Column(name = "status_reason_code") + private String statusReasonCode; + + @Column(name = "status_reason_message") + private String statusReasonMessage; + + @Column(name = "total_count") + private Integer totalCount; + + @Column(name = "completed_count") + private Integer completedCount; + + @Column(name = "sender_id") + private String senderId; + + @Column(name = "receiver_id") + private String receiverId; + + @Column(name = "is_msg_encrypted") + private Boolean isMsgEncrypted; + + @Column(name = "meta") + private String meta; + + @Column(name = "transaction_id") + private String transactionId; + + @Column(name = "correlation_id") + private String correlationId; + + @Column(name = "registry_type") + private String registryType; + + @Column(name = "protocol") + private String protocol; + + @Column(name = "payload_filename") + private String payloadFilename; + + @Column(name = "inbound_filename") + private String inboundFilename; + + @Column(name = "outbound_filename") + private String outboundFilename; + + @Column(insertable = false, updatable = false) + private Timestamp createdDate; + + @Column(insertable = false) + private Timestamp lastUpdatedDate; + + @ToString.Exclude + @OneToMany(mappedBy = "responseTrackerEntity", cascade = CascadeType.ALL) + private List responseDataEntityList = new ArrayList<>(); +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/repository/ResponseDataRepository.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/repository/ResponseDataRepository.java new file mode 100644 index 0000000..9c6552d --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/repository/ResponseDataRepository.java @@ -0,0 +1,14 @@ +package g2pc.dc.core.lib.repository; + +import g2pc.dc.core.lib.entity.ResponseDataEntity; +import g2pc.dc.core.lib.entity.ResponseTrackerEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface ResponseDataRepository extends JpaRepository { + + Optional findByReferenceId(String referenceId); + + Optional findByResponseTrackerEntity(ResponseTrackerEntity responseTrackerEntity); +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/repository/ResponseTrackerRepository.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/repository/ResponseTrackerRepository.java new file mode 100644 index 0000000..a137d69 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/repository/ResponseTrackerRepository.java @@ -0,0 +1,14 @@ +package g2pc.dc.core.lib.repository; + +import g2pc.dc.core.lib.entity.ResponseTrackerEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +public interface ResponseTrackerRepository extends JpaRepository { + + Optional findByTransactionId(String transactionId); + + Optional> findAllByAction(String action); +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/RequestBuilderService.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/RequestBuilderService.java new file mode 100644 index 0000000..65f9f9a --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/RequestBuilderService.java @@ -0,0 +1,61 @@ +package g2pc.dc.core.lib.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.security.TokenExpiryDto; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.search.message.request.SearchCriteriaDTO; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.TxnStatusRequestDTO; +import g2pc.core.lib.enums.ActionsENUM; +import g2pc.core.lib.exceptions.G2pcError; +import kong.unirest.UnirestException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface RequestBuilderService { + + List> createQueryMap(List> payloadMapList, Set> entrySet) throws JsonProcessingException; + + SearchCriteriaDTO getSearchCriteriaDTO(Map queryParamsMap, Map registrySpecificConfigMap); + + RequestMessageDTO buildMessage(List searchCriteriaDTOList); + + HeaderDTO buildHeader(ActionsENUM txnType) throws JsonProcessingException; + + String buildRequest(List searchCriteriaDTOList, String transactionId, ActionsENUM txnType) throws JsonProcessingException; + + G2pcError sendRequest(String requestString, String uri, String clientId, String clientSecret, + String keyClockClientTokenUrl, boolean isEncrypt, boolean isSign, InputStream fis, + String encryptedSalt, String p12Password, String txnType) throws Exception; + + CacheDTO createCache(String data, String status, String protocol); + + void saveCache(CacheDTO cacheDTO, String cacheKey) throws JsonProcessingException; + + public void saveToken(String cacheKey, TokenExpiryDto tokenExpiryDto) throws JsonProcessingException; + + public TokenExpiryDto getTokenFromCache(String clientId) throws JsonProcessingException; + + public String getValidatedToken(String keyCloakUrl, String clientId, String clientSecret) throws IOException, UnirestException, ParseException; + + + TxnStatusRequestDTO buildTransactionRequest(String transactionID, String transactionType); + + String buildStatusRequest(TxnStatusRequestDTO txnStatusRequestDTO, String transactionId, ActionsENUM txnType) throws JsonProcessingException; + + StatusRequestMessageDTO buildStatusRequestMessage(TxnStatusRequestDTO txnStatusRequestDTO); + + List> generatePayloadFromCsv(File payloadFile); + + G2pcError sendRequestSftp(String requestString, boolean isEncrypt, boolean isSign, InputStream fis, + String encryptedSalt, String p12Password, String txnType, + SftpServerConfigDTO sftpServerConfigDTO,String sendFilename) throws Exception; +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/ResponseHandlerService.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/ResponseHandlerService.java new file mode 100644 index 0000000..fab2e80 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/ResponseHandlerService.java @@ -0,0 +1,18 @@ +package g2pc.dc.core.lib.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.exceptions.G2pcValidationException; + +/** + * The interface Response handler service. + */ +public interface ResponseHandlerService { + + /** + * Update cache. + * + * @param cacheKey the cache key + * @throws JsonProcessingException the json processing exception + */ + void updateCache(String cacheKey) throws JsonProcessingException; +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/TxnTrackerService.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/TxnTrackerService.java new file mode 100644 index 0000000..95d1de0 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/service/TxnTrackerService.java @@ -0,0 +1,35 @@ +package g2pc.dc.core.lib.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.exceptions.G2pcError; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public interface TxnTrackerService { + + void saveInitialTransaction(List> payloadMapList, String transactionId, String status, String protocol) throws JsonProcessingException; + + void saveRequestTransaction(String requestString, String regType, String transactionId, String protocol) throws JsonProcessingException; + + CacheDTO createCache(String data, String status, String protocol); + + void saveCache(CacheDTO cacheDTO, String cacheKey) throws JsonProcessingException; + + G2pcError saveRequestInDB(String requestString, String regType, String protocol, + G2pcError g2pcError, String payloadFilename, + String inboundFilename, Boolean sunbirdEnabled) throws IOException; + + G2pcError updateTransactionDbAndCache(ResponseDTO responseDTO, String outboundFilename, Boolean sunbirdEnabled) throws IOException; + + void saveInitialStatusTransaction(String txnType, String transactionId, String status, String protocol) throws JsonProcessingException; + + G2pcError saveRequestInStatusDB(String requestString, String regType) throws IOException; + + G2pcError updateStatusTransactionDbAndCache(StatusResponseDTO statusResponseDTO) throws IOException; + +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/RequestBuilderServiceImpl.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/RequestBuilderServiceImpl.java new file mode 100644 index 0000000..1c2b634 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/RequestBuilderServiceImpl.java @@ -0,0 +1,542 @@ +package g2pc.dc.core.lib.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.config.G2pUnirestHelper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.constants.G2pSecurityConstants; +import g2pc.core.lib.constants.SftpConstants; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.MetaDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.common.security.G2pTokenResponse; +import g2pc.core.lib.dto.common.security.TokenExpiryDto; +import g2pc.core.lib.dto.search.message.request.*; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.TxnStatusRequestDTO; +import g2pc.core.lib.enums.*; +import g2pc.core.lib.exceptionhandler.ErrorResponse; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.security.service.AsymmetricSignatureService; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import g2pc.core.lib.security.service.G2pTokenService; +import g2pc.core.lib.security.service.G2pcUtilityClass; +import g2pc.core.lib.service.SftpHandlerService; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dc.core.lib.service.RequestBuilderService; +import kong.unirest.HttpResponse; +import kong.unirest.UnirestException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.text.ParseException; + +@Service +@Slf4j +public class RequestBuilderServiceImpl implements RequestBuilderService { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + G2pUnirestHelper g2pUnirestHelper; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Autowired + G2pTokenService g2pTokenService; + + @Autowired + AsymmetricSignatureService asymmetricSignatureService; + + @Autowired + private SftpHandlerService sftpHandlerService; + + @Autowired + G2pcUtilityClass utility; + + + /** + * Create a query from payload + * + * @param payloadMapList required query params data + * @param entrySet required query params map to be matched in config + * @return query map + */ + @SuppressWarnings("unchecked") + @Override + public List> createQueryMap(List> payloadMapList, Set> entrySet) throws JsonProcessingException { + + ObjectMapper objectMapper = new ObjectMapper(); + List> queryList = new ArrayList<>(); + for (Map payloadMap : payloadMapList) { + Map queryMap = new HashMap<>(); + for (Map.Entry entry : entrySet) { + Map queryParamsMap = objectMapper.readValue(objectMapper.writeValueAsString(entry.getValue()), HashMap.class); + + queryParamsMap.forEach((key, value) -> { + if (payloadMap.containsKey(key)) { + + queryParamsMap.put(key, payloadMap.get(key)); + } + }); + queryMap.put(entry.getKey(), queryParamsMap); + } + queryList.add(queryMap); + } + return queryList; + } + + + /** + * Create a search criteria from query params + * + * @param queryParamsMap required + * @param registrySpecificConfigMap required + * @return SearchCriteriaDTO + */ + @Override + public SearchCriteriaDTO getSearchCriteriaDTO(Map queryParamsMap, Map registrySpecificConfigMap) { + RequestPaginationDTO paginationDTO = new RequestPaginationDTO(10, 1); + ConsentDTO consentDTO = new ConsentDTO(); + AuthorizeDTO authorizeDTO = new AuthorizeDTO(); + List sortDTOList = new ArrayList<>(); + SortDTO sortDTO = new SortDTO(); + sortDTO.setAttributeName(registrySpecificConfigMap.get("sort_attribute").toString()); + sortDTO.setSortOrder(registrySpecificConfigMap.get("sort_order").toString()); + sortDTOList.add(sortDTO); + return utility.getSearchCriteriaDTO(queryParamsMap, registrySpecificConfigMap, sortDTOList, paginationDTO, consentDTO, authorizeDTO); + + } + + + + /** + * Create message component of request + * + * @param searchCriteriaDTOList required + * @return MessageDTO + */ + @Override + public RequestMessageDTO buildMessage(List searchCriteriaDTOList) { + List searchRequestDTOList = new ArrayList<>(); + for (SearchCriteriaDTO searchCriteriaDTO : searchCriteriaDTOList) { + SearchRequestDTO searchRequestDTO = new SearchRequestDTO(); + searchRequestDTO.setReferenceId(CommonUtils.generateUniqueId("R")); + searchRequestDTO.setTimestamp(CommonUtils.getCurrentTimeStamp()); + searchRequestDTO.setLocale(LocalesENUM.EN.toValue()); + searchRequestDTO.setSearchCriteria(searchCriteriaDTO); + searchRequestDTOList.add(searchRequestDTO); + } + RequestMessageDTO messageDTO = new RequestMessageDTO(); + messageDTO.setTransactionId(CommonUtils.generateUniqueId("T")); + messageDTO.setSearchRequest(searchRequestDTOList); + return messageDTO; + } + + + /** + * Create header component of request + * @param txnType txnType is used to identify transaction type for header creation + * @return headerDto is returning. + */ + @Override + public HeaderDTO buildHeader(ActionsENUM txnType) { + RequestHeaderDTO requestHeaderDTO = new RequestHeaderDTO(); + requestHeaderDTO.setMessageId(CommonUtils.generateUniqueId("M")); + requestHeaderDTO.setMessageTs(CommonUtils.getCurrentTimeStamp()); + requestHeaderDTO.setAction(txnType.toValue()); + requestHeaderDTO.setSenderId("spp.example.org"); + requestHeaderDTO.setReceiverId("pymts.example.org"); + requestHeaderDTO.setTotalCount(21800); + requestHeaderDTO.setIsMsgEncrypted(false); + Map metaMap = new HashMap<>(); + MetaDTO metaDTO = new MetaDTO(); + metaDTO.setData(metaMap); + requestHeaderDTO.setMeta(metaDTO); + requestHeaderDTO.setSenderUri("https://spp.example.org/{namespace}/callback/on-search"); + return requestHeaderDTO; + } + + /** + * Create whole request from message and header + * + * @param searchCriteriaDTOList searchCriteriaDTOList to save in messageDto + * @param transactionId transactionId to set in messageDto + * @return request + */ + @Override + public String buildRequest(List searchCriteriaDTOList, String transactionId, ActionsENUM txnType) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + RequestMessageDTO messageDTO = buildMessage(searchCriteriaDTOList); + messageDTO.setTransactionId(transactionId); + HeaderDTO headerDTO = buildHeader(txnType); + RequestDTO requestDTO = new RequestDTO(); + requestDTO.setSignature("new signature to be generated for request"); + requestDTO.setHeader(headerDTO); + requestDTO.setMessage(messageDTO); + + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(requestDTO); + } + + /** + * + * @param requestString requestString which need to be sent + * @param uri uri to which communication need to happen. + * @param clientId keycloak clientId + * @param clientSecret keycloak clientSecret + * @param keyClockClientTokenUrl keycloak ClientTokenUrl + * @param isEncrypt encryption flag + * @param isSign signature flag + * @param fis .p12 file input stream + * @param p12Password p12Password + * @param txnType txnType + * @return G2pcError will return + * @throws Exception Exception might get thrown from this method + */ + @Override + public G2pcError sendRequest(String requestString, String uri, String clientId, String clientSecret, + String keyClockClientTokenUrl, boolean isEncrypt, boolean isSign, InputStream fis, + String encryptedSalt, String p12Password, String txnType) throws Exception { + log.info("Is encrypted ? -> " + isEncrypt); + log.info("Is signed ? -> " + isSign); + ObjectMapper objectMapper = new ObjectMapper(); + requestString = createSignature(isEncrypt, isSign, requestString, fis, encryptedSalt, p12Password, txnType); + String jwtToken = getValidatedToken(keyClockClientTokenUrl, clientId, clientSecret); + log.info("Updated Request -> " + requestString); + HttpResponse response = g2pUnirestHelper.g2pPost(uri) + .header("Content-Type", "application/json") + .header("Authorization", jwtToken) + .body(requestString) + .asString(); + G2pcError g2pcError = null; + if (response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR.value()) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), response.getBody()); + log.info("Exception is thrown by search endpoint", response.getStatus()); + } else if (response.getStatus() == HttpStatus.UNAUTHORIZED.value()) { + ErrorResponse errorResponse = objectMapper.readerFor(ErrorResponse.class). + readValue(response.getBody()); + g2pcError = errorResponse.getG2PcError(); + } else if (response.getStatus() == HttpStatus.BAD_REQUEST.value()) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_BAD_REQUEST.toValue(), response.getBody()); + } else if (response.getStatus() != HttpStatus.OK.value()) { + ErrorResponse errorResponse = objectMapper.readerFor(ErrorResponse.class). + readValue(response.getBody()); + g2pcError = errorResponse.getG2PcError(); + } else if (response.getStatus() == HttpStatus.OK.value()) { + g2pcError = new G2pcError(HeaderStatusENUM.SUCC.toValue(), "request saved in cache"); + + } else { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), HttpStatus.INTERNAL_SERVER_ERROR.toString()); + } + log.info("request send response status = {}", response.getStatus()); + return g2pcError; + } + + /** + * + * @param data data to send + * @param status status to set + * @param protocol protocol to set + * @return cacheDto + */ + @Override + public CacheDTO createCache(String data, String status, String protocol) { + log.info("Save requests in cache with status pending"); + CacheDTO cacheDTO = new CacheDTO(); + cacheDTO.setData(data); + cacheDTO.setStatus(status); + cacheDTO.setProtocol(protocol); + cacheDTO.setCreatedDate(CommonUtils.getCurrentTimeStamp()); + cacheDTO.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + return cacheDTO; + } + + /** + * + * @param cacheDTO cacheDTO to save + * @param cacheKey cacheKey refer to save cacheDTO + * @throws JsonProcessingException + */ + @Override + public void saveCache(CacheDTO cacheDTO, String cacheKey) throws JsonProcessingException { + ValueOperations val = redisTemplate.opsForValue(); + val.set(cacheKey, new ObjectMapper().writeValueAsString(cacheDTO)); + } + + /** + * Method to save token in cache + * + * @param cacheKey cacheKey on which tokenExpiryDto is going to store + * @param tokenExpiryDto tokenExpiryDto which is being stored + * @throws JsonProcessingException + */ + @Override + public void saveToken(String cacheKey, TokenExpiryDto tokenExpiryDto) throws JsonProcessingException { + ValueOperations val = redisTemplate.opsForValue(); + val.set(cacheKey, new ObjectMapper().writeValueAsString(tokenExpiryDto)); + } + + /** + * Method to get token from cache + * + * @param clientId clientId by which token is going to extract + * @return tokenExpiryDto is going to return + * @throws JsonProcessingException JsonProcessingException is might be thrown from this method + */ + @Override + public TokenExpiryDto getTokenFromCache(String clientId) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + Set redisKeys = this.redisTemplate.keys(clientId); + List cacheKeysList = new ArrayList((Collection) Objects.requireNonNull(redisKeys)); + if (!cacheKeysList.isEmpty()) { + String cacheKey = cacheKeysList.get(0); + String tokenData = (String) this.redisTemplate.opsForValue().get(cacheKey); + TokenExpiryDto tokenExpiryDto = objectMapper.readerFor(TokenExpiryDto.class).readValue(tokenData); + return tokenExpiryDto; + } + return null; + } + + + /** + * Method to get validated token + * + * @param keyCloakUrl keycloak url + * @param clientId client id for token + * @param clientSecret client secret + * @return string which is token + * @throws IOException which can be thrown + * @throws UnirestException which can be thrown + * @throws ParseException which can be thrown + */ + @Override + public String getValidatedToken(String keyCloakUrl, String clientId, String clientSecret) throws IOException, UnirestException, ParseException { + TokenExpiryDto tokenExpiryDto = getTokenFromCache(clientId); + + String jwtToken = ""; + if (Boolean.TRUE.equals(g2pTokenService.isTokenExpired(tokenExpiryDto))) { + G2pTokenResponse tokenResponse = g2pTokenService.getToken(keyCloakUrl, clientId, clientSecret); + jwtToken = tokenResponse.getAccessToken(); + saveToken(clientId + "-token", g2pTokenService.createTokenExpiryDto(tokenResponse)); + } else { + jwtToken = tokenExpiryDto.getToken(); + } + return jwtToken; + } + + + /** + * This method is used to create signature and add appropriate message in request body according to configurations. + * + * @param isEncrypt flag of encryption + * @param isSign flag of signature + * @param requestString created request in string format + * @return created signature in string format + * @throws Exception exception is thrown general because there more than 6 exception being thrown by this method. + */ + @SuppressWarnings("unchecked") + private String createSignature(boolean isEncrypt, boolean isSign, + String requestString, InputStream fis, String encryptionSalt, + String p12Password, String txnType) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + String messageString = ""; + if (txnType.equals(CoreConstants.STATUS_TXN_TYPE)) { + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + messageString = objectMapper.writeValueAsString(statusRequestMessageDTO); + } else { + RequestMessageDTO requestMessageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + messageString = objectMapper.writeValueAsString(requestMessageDTO); + } + + String requestHeaderString; + String signature = null; + + if (isSign) { + if (isEncrypt) { + String encryptedMessageString = encryptDecrypt.g2pEncrypt(messageString, G2pSecurityConstants.SECRET_KEY); + requestDTO.setMessage(encryptionSalt + encryptedMessageString); + requestDTO.getHeader().setIsMsgEncrypted(true); + Map meta = (Map) requestDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, true); + requestDTO.getHeader().getMeta().setData(meta); + requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + byte[] asymmetricSignature = asymmetricSignatureService.sign(requestHeaderString + encryptedMessageString, fis, p12Password); + signature = Base64.getEncoder().encodeToString(asymmetricSignature); + log.info("Encrypted message ->" + encryptedMessageString); + log.info("Hashed Signature ->" + signature); + } else { + requestDTO.getHeader().setIsMsgEncrypted(false); + Map meta = (Map) requestDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, true); + requestDTO.getHeader().getMeta().setData(meta); + requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + byte[] asymmetricSignature = asymmetricSignatureService.sign(requestHeaderString + messageString, fis, p12Password); + signature = Base64.getEncoder().encodeToString(asymmetricSignature); + log.info("Hashed Signature ->" + signature); + } + } else { + if (isEncrypt) { + String encryptedMessageString = encryptDecrypt.g2pEncrypt(messageString, G2pSecurityConstants.SECRET_KEY); + requestDTO.setMessage(encryptionSalt + encryptedMessageString); + requestDTO.getHeader().setIsMsgEncrypted(true); + Map meta = (Map) requestDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, false); + requestDTO.getHeader().getMeta().setData(meta); + log.info("Encrypted message ->" + encryptedMessageString); + } else { + requestDTO.getHeader().setIsMsgEncrypted(false); + Map meta = (Map) requestDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, false); + requestDTO.getHeader().getMeta().setData(meta); + } + } + requestDTO.setSignature(signature); + requestString = objectMapper.writeValueAsString(requestDTO); + return requestString; + } + + /** + * + * @param transactionID transactionID used to build transactionRequest of /status + * @param transactionType transaction type for which transaction request is building + * @return txnStatusRequestDTO + */ + @Override + public TxnStatusRequestDTO buildTransactionRequest(String transactionID, String transactionType) { + + TxnStatusRequestDTO txnStatusRequestDTO = new TxnStatusRequestDTO(); + if (transactionType.equals(StatusTransactionTypeEnum.SEARCH.toValue())) { + txnStatusRequestDTO.setAttributeType(AttributeTypeEnum.TRANSACTION_ID.toValue()); + txnStatusRequestDTO.setTxnType(StatusTransactionTypeEnum.SEARCH.toValue()); + txnStatusRequestDTO.setAttributeValue(transactionID); + } + return txnStatusRequestDTO; + } + + /** + * + * @param txnStatusRequestDTO txnStatusRequestDTO to build status request + * @param transactionId transactionId for which request is building + * @param txnType txnType for which request is building + * @return string + * @throws JsonProcessingException exception which is going thrown + */ + @Override + public String buildStatusRequest(TxnStatusRequestDTO txnStatusRequestDTO, String transactionId, ActionsENUM txnType) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + HeaderDTO headerDTO = buildHeader(txnType); + StatusRequestMessageDTO messageDTO = buildStatusRequestMessage(txnStatusRequestDTO); + messageDTO.setTransactionId(transactionId); + StatusRequestDTO statusRequestDTO = new StatusRequestDTO(); + statusRequestDTO.setSignature("new signature to be generated for request"); + statusRequestDTO.setHeader(headerDTO); + statusRequestDTO.setMessage(messageDTO); + + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(statusRequestDTO); + } + + /** + * + * @param txnStatusRequestDTO txnStatusRequestDTO for which status request is creating + * @return StatusRequestMessageDTO + */ + @Override + public StatusRequestMessageDTO buildStatusRequestMessage(TxnStatusRequestDTO txnStatusRequestDTO) { + StatusRequestMessageDTO statusRequestMessageDTO = new StatusRequestMessageDTO(); + statusRequestMessageDTO.setTxnStatusRequest(txnStatusRequestDTO); + return statusRequestMessageDTO; + } + + /** + * Create a payload for request + * @param payloadFile csv file containing query params data + * @return List> of payload + */ + @Override + public List> generatePayloadFromCsv(File payloadFile) { + List> payloadMapList = new ArrayList<>(); + Reader reader = null; + try { + reader = new BufferedReader(new FileReader(payloadFile)); + CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT); + List csvRecordList = csvParser.getRecords(); + CSVRecord headerRecord = csvRecordList.get(0); + List headerList = new ArrayList<>(); + for (int i = 0; i < headerRecord.size(); i++) { + headerList.add(headerRecord.get(i)); + } + for (int i = 1; i < csvRecordList.size(); i++) { + CSVRecord csvRecord = csvRecordList.get(i); + Map payloadMap = new HashMap<>(); + for (int j = 0; j < headerRecord.size(); j++) { + payloadMap.put(headerList.get(j), csvRecord.get(j)); + } + payloadMapList.add(payloadMap); + } + } catch (IOException e) { + log.error("Error converting to payload : ", e); + } + return payloadMapList; + } + + /** + * + * @param requestString requestString which need to be sent + * @param isEncrypt encryption flag + * @param isSign signature flag + * @param fis .p12 file input stream + * @param p12Password p12Password + * @param txnType txnType + * @param sftpServerConfigDTO SftpServerConfigDTO + * @return G2pcError will return + * @throws Exception Exception might get thrown from this method + */ + @Override + public G2pcError sendRequestSftp(String requestString, boolean isEncrypt, boolean isSign, + InputStream fis, String encryptedSalt, String p12Password, + String txnType, SftpServerConfigDTO sftpServerConfigDTO,String sendFilename) throws Exception { + + log.info("Is encrypted ? -> " + isEncrypt); + log.info("Is signed ? -> " + isSign); + requestString = createSignature(isEncrypt, isSign, requestString, fis, encryptedSalt, p12Password, txnType); + log.info("Updated Request -> " + requestString); + + Path tempFile = Paths.get(System.getProperty("java.io.tmpdir"), sendFilename); + Files.createFile(tempFile); + Files.write(tempFile, requestString.getBytes()); + + Boolean status = sftpHandlerService.uploadFileToSftp(sftpServerConfigDTO, tempFile.toString(), + sftpServerConfigDTO.getRemoteInboundDirectory()); + Files.delete(tempFile); + G2pcError g2pcError; + if (Boolean.FALSE.equals(status)) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), SftpConstants.UPLOAD_ERROR_MESSAGE); + log.error(SftpConstants.UPLOAD_ERROR_MESSAGE); + }else { + g2pcError = new G2pcError(HeaderStatusENUM.SUCC.toValue(), SftpConstants.UPLOAD_SUCCESS_MESSAGE); + } + return g2pcError; + } +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/ResponseHandlerServiceImpl.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/ResponseHandlerServiceImpl.java new file mode 100644 index 0000000..7279d1c --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/ResponseHandlerServiceImpl.java @@ -0,0 +1,65 @@ +package g2pc.dc.core.lib.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.JsonSchema; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseMessageDTO; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dc.core.lib.constants.DcConstants; +import g2pc.dc.core.lib.service.ResponseHandlerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.apache.commons.codec.Resources.getInputStream; + +/** + * The type Response handler service. + */ +@Service +@Slf4j +public class ResponseHandlerServiceImpl implements ResponseHandlerService { + + + + @Autowired + private RedisTemplate redisTemplate; + + /** + * + * @param cacheKey the cache key to update data + * @throws JsonProcessingException exception might be thrown + */ + @Override + public void updateCache(String cacheKey) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + CacheDTO cacheDTO = objectMapper.readerFor(CacheDTO.class).readValue(redisTemplate.opsForValue().get(cacheKey)); + + cacheDTO.setStatus(DcConstants.COMPLETED); + cacheDTO.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + + ValueOperations val = redisTemplate.opsForValue(); + val.set(cacheKey, new ObjectMapper().writeValueAsString(cacheDTO)); + } + + + +} diff --git a/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/TxnTrackerServiceImpl.java b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/TxnTrackerServiceImpl.java new file mode 100644 index 0000000..9f3ea63 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/java/g2pc/dc/core/lib/serviceimpl/TxnTrackerServiceImpl.java @@ -0,0 +1,516 @@ +package g2pc.dc.core.lib.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.request.SearchRequestDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.search.message.response.SearchResponseDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.TxnStatusRequestDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseMessageDTO; +import g2pc.core.lib.dto.status.message.response.TxnStatusResponseDTO; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.service.ElasticsearchService; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dc.core.lib.constants.DcConstants; +import g2pc.dc.core.lib.dto.ResponseDataDto; +import g2pc.dc.core.lib.dto.ResponseTrackerDto; +import g2pc.dc.core.lib.entity.ResponseDataEntity; +import g2pc.dc.core.lib.entity.ResponseTrackerEntity; +import g2pc.dc.core.lib.repository.ResponseDataRepository; +import g2pc.dc.core.lib.repository.ResponseTrackerRepository; +import g2pc.dc.core.lib.service.TxnTrackerService; +import kong.unirest.HttpResponse; +import kong.unirest.JsonNode; +import kong.unirest.Unirest; +import lombok.extern.slf4j.Slf4j; +import org.elasticsearch.action.search.SearchResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import com.fasterxml.jackson.core.type.TypeReference; + +import java.io.IOException; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +@Slf4j +public class TxnTrackerServiceImpl implements TxnTrackerService { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private ResponseTrackerRepository responseTrackerRepository; + + @Autowired + private ResponseDataRepository responseDataRepository; + + @Autowired + private ResponseHandlerServiceImpl responseHandlerService; + + @Autowired + private ElasticsearchService elasticsearchService; + + @Value("${sunbird.api_urls.response_data_api}") + private String responseDataURL; + + @Value("${sunbird.api_urls.response_tracker_api}") + private String responseTrackerURL; + + /** + * Save initial payload to Redis and create a new transaction in DB table + * + * @param payloadMapList payloadMap list to save in initial transaction + * @param transactionId transactionId to store in initial transaction + * @param status status to store in initial transaction + */ + @Override + public void saveInitialTransaction(List> payloadMapList, String transactionId, String status, String protocol) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + CacheDTO cacheDTO = createCache(objectMapper.writeValueAsString(payloadMapList), HeaderStatusENUM.PDNG.toValue(), protocol); + saveCache(cacheDTO, "initial-" + transactionId); + } + + /** + * Save request transactions to Redis + * + * @param requestString string to store in cache + * @param regType regType to store in cache + * @param transactionId transactionId to store cache + */ + @Override + public void saveRequestTransaction(String requestString, String regType, String transactionId, String protocol) throws JsonProcessingException { + CacheDTO cacheDTO = createCache(requestString, HeaderStatusENUM.PDNG.toValue(), protocol); + saveCache(cacheDTO, regType + "-" + transactionId); + } + + /** + * Create cache to save in Redis + * + * @param data data to store in cache + * @param status status to store in cache + * @return CacheDTO + */ + @Override + public CacheDTO createCache(String data, String status, String protocol) { + log.info("Save requests in cache with status pending"); + CacheDTO cacheDTO = new CacheDTO(); + cacheDTO.setData(data); + cacheDTO.setStatus(status); + cacheDTO.setProtocol(protocol); + cacheDTO.setCreatedDate(CommonUtils.getCurrentTimeStamp()); + cacheDTO.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + return cacheDTO; + } + + /** + * Save cache in Redis + * + * @param cacheDTO cacheDTO + * @param cacheKey cacheKey key for which data stored + */ + @Override + public void saveCache(CacheDTO cacheDTO, String cacheKey) throws JsonProcessingException { + ValueOperations val = redisTemplate.opsForValue(); + val.set(cacheKey, new ObjectMapper().writeValueAsString(cacheDTO)); + } + + /** + * @param requestString request string to convert it in statusRequestDto + * @param regType regType to store in db + * @param protocol protocol to save in db + * @throws JsonProcessingException + */ + @Override + public G2pcError saveRequestInDB(String requestString, String regType, String protocol, + G2pcError g2pcError, String payloadFilename, + String inboundFilename, Boolean sunbirdEnabled) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class, HeaderDTO.class); + + RequestDTO requestDTO = objectMapper.readValue(requestString, RequestDTO.class); + HeaderDTO headerDTO = requestDTO.getHeader(); + RequestMessageDTO messageDTO = objectMapper.convertValue(requestDTO.getMessage(), RequestMessageDTO.class); + String transactionId = messageDTO.getTransactionId(); + g2pcError = new G2pcError(HttpStatus.OK.toString(), "Successfully stored in db"); + if (Boolean.TRUE.equals(sunbirdEnabled)) { + Map fieldValues = new HashMap<>(); + fieldValues.put("transaction_id.keyword", transactionId); + SearchResponse response = elasticsearchService.exactSearch("response_tracker", fieldValues); + if (response.getHits().getHits().length > 0) { + log.info("response: {}", response.getHits().getHits()[0].getSourceAsString()); + } else { + ResponseTrackerDto responseTrackerDto = new ResponseTrackerDto(); + responseTrackerDto.setVersion(headerDTO.getVersion()); + responseTrackerDto.setMessageId(headerDTO.getMessageId()); + responseTrackerDto.setMessageTs(headerDTO.getMessageTs()); + responseTrackerDto.setAction(headerDTO.getAction()); + responseTrackerDto.setSenderId(headerDTO.getSenderId()); + responseTrackerDto.setReceiverId(headerDTO.getReceiverId()); + responseTrackerDto.setIsMsgEncrypted(headerDTO.getIsMsgEncrypted()); + responseTrackerDto.setTransactionId(transactionId); + responseTrackerDto.setRegistryType(regType); + responseTrackerDto.setProtocol(protocol); + responseTrackerDto.setPayloadFilename(payloadFilename); + responseTrackerDto.setInboundFilename(inboundFilename); + List searchRequestDTOList = messageDTO.getSearchRequest(); + for (SearchRequestDTO searchRequestDTO : searchRequestDTOList) { + ResponseDataDto responseDataDto = new ResponseDataDto(); + responseDataDto.setRegistryTransactionsId(transactionId); + responseDataDto.setReferenceId(searchRequestDTO.getReferenceId()); + responseDataDto.setTimestamp(searchRequestDTO.getTimestamp()); + responseDataDto.setVersion(searchRequestDTO.getSearchCriteria().getVersion()); + responseDataDto.setRegType(searchRequestDTO.getSearchCriteria().getRegType()); + responseDataDto.setRegSubType(searchRequestDTO.getSearchCriteria().getRegSubType()); + responseDataDto.setStatus(HeaderStatusENUM.PDNG.toValue()); + responseDataDto.setStatusReasonCode(g2pcError.getCode()); + String responseDataString = objectMapper.writeValueAsString(responseDataDto); + HttpResponse responseData = Unirest.post(responseDataURL) + .header("Content-Type", "application/json") + .body(responseDataString) + .asJson(); + log.info("ResponseData entity response-> " + responseData); + if (responseData.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseData.getBody().toString()); + } + } + String responseTrackerString = objectMapper.writeValueAsString(responseTrackerDto); + HttpResponse responseT = Unirest.post(responseTrackerURL) + .header("Content-Type", "application/json") + .body(responseTrackerString) + .asJson(); + if (responseT.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseT.getBody().toString()); + } + log.info("ResponseTracker entity response-> " + response); + } + } else { + try { + Optional responseTrackerEntityOptional = responseTrackerRepository.findByTransactionId(transactionId); + if (responseTrackerEntityOptional.isEmpty()) { + ResponseTrackerEntity responseTrackerEntity = new ResponseTrackerEntity(); + responseTrackerEntity.setVersion(headerDTO.getVersion()); + responseTrackerEntity.setMessageId(headerDTO.getMessageId()); + responseTrackerEntity.setMessageTs(headerDTO.getMessageTs()); + responseTrackerEntity.setAction(headerDTO.getAction()); + responseTrackerEntity.setSenderId(headerDTO.getSenderId()); + responseTrackerEntity.setReceiverId(headerDTO.getReceiverId()); + responseTrackerEntity.setIsMsgEncrypted(headerDTO.getIsMsgEncrypted()); + responseTrackerEntity.setTransactionId(transactionId); + responseTrackerEntity.setRegistryType(regType); + responseTrackerEntity.setProtocol(protocol); + responseTrackerEntity.setPayloadFilename(payloadFilename); + responseTrackerEntity.setInboundFilename(inboundFilename); + List searchRequestDTOList = messageDTO.getSearchRequest(); + for (SearchRequestDTO searchRequestDTO : searchRequestDTOList) { + ResponseDataEntity responseDataEntity = new ResponseDataEntity(); + responseDataEntity.setReferenceId(searchRequestDTO.getReferenceId()); + responseDataEntity.setTimestamp(searchRequestDTO.getTimestamp()); + responseDataEntity.setVersion(searchRequestDTO.getSearchCriteria().getVersion()); + responseDataEntity.setRegType(searchRequestDTO.getSearchCriteria().getRegType()); + responseDataEntity.setRegSubType(searchRequestDTO.getSearchCriteria().getRegSubType()); + responseDataEntity.setStatus(HeaderStatusENUM.PDNG.toValue()); + responseDataEntity.setResponseTrackerEntity(responseTrackerEntity); + responseDataEntity.setStatusReasonCode(g2pcError.getCode()); + responseTrackerEntity.getResponseDataEntityList().add(responseDataEntity); + } + responseTrackerRepository.save(responseTrackerEntity); + } + } catch (Exception e) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), e.getMessage()); + } + } + return g2pcError; + } + + /** + * @param responseDTO responseDTO + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public G2pcError updateTransactionDbAndCache(ResponseDTO responseDTO, String outboundFilename, Boolean sunbirdEnabled) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(ResponseHeaderDTO.class, HeaderDTO.class); + G2pcError g2pcError; + ResponseHeaderDTO headerDTO = objectMapper.convertValue(responseDTO.getHeader(), ResponseHeaderDTO.class); + ResponseMessageDTO messageDTO = objectMapper.convertValue(responseDTO.getMessage(), ResponseMessageDTO.class); + String transactionId = messageDTO.getTransactionId(); + List searchResponseDTOList = messageDTO.getSearchResponse(); + if (Boolean.TRUE.equals(sunbirdEnabled)) { + Map fieldValues = new HashMap<>(); + fieldValues.put("transaction_id.keyword", transactionId); + SearchResponse responseTrackerSearchResponse = elasticsearchService.exactSearch("response_tracker", fieldValues); + if (responseTrackerSearchResponse.getHits().getHits().length > 0) { + log.info("response: {}", responseTrackerSearchResponse.getHits().getHits()[0].getSourceAsString()); + String responseTrackerDtoString = responseTrackerSearchResponse.getHits().getHits()[0].getSourceAsString(); + Map resultMap = objectMapper.readValue(responseTrackerDtoString, new TypeReference>() { + }); + + String osid = resultMap.get(DcConstants.OSID).toString().substring(2); + ResponseTrackerDto responseTrackerDto = objectMapper.readerFor(ResponseTrackerDto.class). + readValue(responseTrackerDtoString); + String cacheKey = responseTrackerDto.getRegistryType() + "-" + transactionId; + for (SearchResponseDTO searchResponseDTO : searchResponseDTOList) { + Map dataFieldValues = new HashMap<>(); + dataFieldValues.put("reference_id.keyword", searchResponseDTO.getReferenceId()); + SearchResponse responseDataSearchResponse = elasticsearchService.exactSearch("response_data", dataFieldValues); + if (responseDataSearchResponse.getHits().getHits().length > 0) { + String responseDataDtoString = responseDataSearchResponse.getHits().getHits()[0].getSourceAsString(); + Map responseDataResultMap = objectMapper.readValue(responseDataDtoString, new TypeReference>() { + }); + String responseDataOsId = responseDataResultMap.get(DcConstants.OSID).toString().substring(2); + ResponseDataDto responseDataDto = objectMapper.readerFor(ResponseDataDto.class). + readValue(responseDataDtoString); + responseDataDto.setStatus(searchResponseDTO.getStatus()); + responseDataDto.setStatusReasonCode(searchResponseDTO.getStatusReasonCode()); + responseDataDto.setStatusReasonMessage(searchResponseDTO.getStatusReasonMessage()); + responseDataDto.setRegSubType(searchResponseDTO.getData().getRegSubType()); + responseDataDto.setRegRecordType(searchResponseDTO.getData().getRegRecordType()); + responseDataDto.setRegRecords(objectMapper.writeValueAsString(searchResponseDTO.getData().getRegRecords())); + responseDataDto.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + String responseDataString = objectMapper.writeValueAsString(responseDataDto); + HttpResponse responseD = Unirest.put(responseDataURL + "/" + responseDataOsId) + .header("Content-Type", "application/json") + .body(responseDataString) + .asJson(); + if (responseD.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseD.getBody().toString()); + return g2pcError; + } + } + } + responseTrackerDto.setStatus(headerDTO.getStatus()); + responseTrackerDto.setStatusReasonCode(headerDTO.getStatusReasonCode()); + responseTrackerDto.setStatusReasonMessage(headerDTO.getStatusReasonMessage()); + responseTrackerDto.setTotalCount(headerDTO.getTotalCount()); + responseTrackerDto.setCompletedCount(headerDTO.getCompletedCount()); + responseTrackerDto.setCorrelationId(messageDTO.getCorrelationId()); + responseTrackerDto.setMeta(objectMapper.writeValueAsString(headerDTO.getMeta())); + responseTrackerDto.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + responseTrackerDto.setOutboundFilename(outboundFilename); + String responseTrackerString = objectMapper.writeValueAsString(responseTrackerDto); + HttpResponse responseT = Unirest.put(responseTrackerURL + "/" + osid) + .header("Content-Type", "application/json") + .body(responseTrackerString) + .asJson(); + if (responseT.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseT.getBody().toString()); + return g2pcError; + } + responseHandlerService.updateCache(cacheKey); + } + } else { + Optional responseTrackerEntityOptional = responseTrackerRepository.findByTransactionId(transactionId); + if (responseTrackerEntityOptional.isPresent()) { + ResponseTrackerEntity responseTrackerEntity = responseTrackerEntityOptional.get(); + String cacheKey = responseTrackerEntity.getRegistryType() + "-" + transactionId; + + for (SearchResponseDTO searchResponseDTO : searchResponseDTOList) { + Optional entityOptional = responseDataRepository.findByReferenceId(searchResponseDTO.getReferenceId()); + if (entityOptional.isPresent()) { + ResponseDataEntity responseDataEntity = entityOptional.get(); + responseDataEntity.setStatus(searchResponseDTO.getStatus()); + responseDataEntity.setStatusReasonCode(searchResponseDTO.getStatusReasonCode()); + responseDataEntity.setStatusReasonMessage(searchResponseDTO.getStatusReasonMessage()); + responseDataEntity.setRegSubType(searchResponseDTO.getData().getRegSubType()); + responseDataEntity.setRegRecordType(searchResponseDTO.getData().getRegRecordType()); + responseDataEntity.setRegRecords(objectMapper.writeValueAsString(searchResponseDTO.getData().getRegRecords())); + responseDataEntity.setLastUpdatedDate(new Timestamp(System.currentTimeMillis())); + responseDataRepository.save(responseDataEntity); + } + } + responseTrackerEntity.setStatus(headerDTO.getStatus()); + responseTrackerEntity.setStatusReasonCode(headerDTO.getStatusReasonCode()); + responseTrackerEntity.setStatusReasonMessage(headerDTO.getStatusReasonMessage()); + responseTrackerEntity.setTotalCount(headerDTO.getTotalCount()); + responseTrackerEntity.setCompletedCount(headerDTO.getCompletedCount()); + responseTrackerEntity.setCorrelationId(messageDTO.getCorrelationId()); + responseTrackerEntity.setMeta(objectMapper.writeValueAsString(headerDTO.getMeta())); + responseTrackerEntity.setLastUpdatedDate(new Timestamp(System.currentTimeMillis())); + responseTrackerEntity.setOutboundFilename(outboundFilename); + responseTrackerRepository.save(responseTrackerEntity); + responseHandlerService.updateCache(cacheKey); + } + } + return new G2pcError(HttpStatus.OK.toString(), "Successfully stored in db"); + } + + /** + * @param txnType transaction type to store in cache + * @param transactionId to store in cache + * @param status status transactionId to store in cache + * @param protocol protocol to store in cache + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public void saveInitialStatusTransaction(String txnType, String transactionId, String status, String protocol) throws JsonProcessingException { + CacheDTO cacheDTO = createCache(txnType, HeaderStatusENUM.PDNG.toValue(), protocol); + saveCache(cacheDTO, "initial-" + transactionId); + + } + + /** + * @param requestString request string to convert it in statusRequestDto + * @param regType regType to store in db + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public G2pcError saveRequestInStatusDB(String requestString, String regType) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class, HeaderDTO.class); + + StatusRequestDTO statusRequestDTO = objectMapper.readValue(requestString, StatusRequestDTO.class); + HeaderDTO headerDTO = statusRequestDTO.getHeader(); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.convertValue(statusRequestDTO.getMessage(), StatusRequestMessageDTO.class); + String transactionId = statusRequestMessageDTO.getTransactionId(); + G2pcError g2pcError = new G2pcError(HttpStatus.OK.toString(), "Successfully stored in db"); + Map fieldValues = new HashMap<>(); + fieldValues.put("transaction_id.keyword", transactionId); + SearchResponse response = elasticsearchService.exactSearch("response_tracker", fieldValues); + if (response.getHits().getHits().length > 0) { + g2pcError = new G2pcError(HttpStatus.OK.toString(), "Data is already present in the db for transaction id" + transactionId); + log.info("response: {}", response.getHits().getHits()[0].getSourceAsString()); + } else { + ResponseTrackerDto responseTrackerDto = new ResponseTrackerDto(); + responseTrackerDto.setVersion(headerDTO.getVersion()); + responseTrackerDto.setMessageId(headerDTO.getMessageId()); + responseTrackerDto.setMessageTs(headerDTO.getMessageTs()); + responseTrackerDto.setAction(headerDTO.getAction()); + responseTrackerDto.setSenderId(headerDTO.getSenderId()); + responseTrackerDto.setReceiverId(headerDTO.getReceiverId()); + responseTrackerDto.setIsMsgEncrypted(headerDTO.getIsMsgEncrypted()); + responseTrackerDto.setTransactionId(transactionId); + responseTrackerDto.setRegistryType(regType); + + TxnStatusRequestDTO txnStatusRequestDTO = statusRequestMessageDTO.getTxnStatusRequest(); + ResponseDataDto responseDataDto = new ResponseDataDto(); + responseDataDto.setTimestamp(""); + responseDataDto.setVersion(""); + responseDataDto.setRegType(""); + responseDataDto.setRegSubType(""); + responseDataDto.setTxnType(txnStatusRequestDTO.getTxnType()); + responseDataDto.setAttributeType(txnStatusRequestDTO.getAttributeType()); + responseDataDto.setAttributeValue((String) txnStatusRequestDTO.getAttributeValue()); + responseDataDto.setStatus(HeaderStatusENUM.PDNG.toValue()); + + String responseDataString = objectMapper.writeValueAsString(responseDataDto); + HttpResponse responseD = Unirest.post(responseDataURL) + .header("Content-Type", "application/json") + .body(responseDataString) + .asJson(); + if (responseD.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseD.getBody().toString()); + return g2pcError; + } + String responseTrackerString = objectMapper.writeValueAsString(responseTrackerDto); + HttpResponse responseT = Unirest.post(responseTrackerURL) + .header("Content-Type", "application/json") + .body(responseTrackerString) + .asJson(); + log.info("ResponseTracker entity response-> " + responseT); + if (responseT.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseT.getBody().toString()); + return g2pcError; + } + } + return g2pcError; + } + + /** + * @param statusResponseDTO statusResponseDTO used to update transaction db + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public G2pcError updateStatusTransactionDbAndCache(StatusResponseDTO statusResponseDTO) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(ResponseHeaderDTO.class, HeaderDTO.class); + G2pcError g2pcError; + ResponseHeaderDTO headerDTO = objectMapper.convertValue(statusResponseDTO.getHeader(), ResponseHeaderDTO.class); + StatusResponseMessageDTO statusResponseMessageDTO = objectMapper.convertValue(statusResponseDTO.getMessage(), StatusResponseMessageDTO.class); + String transactionId = statusResponseMessageDTO.getTransactionId(); + TxnStatusResponseDTO txnStatusResponseDTO = statusResponseMessageDTO.getTxnStatusResponse(); + Map fieldValues = new HashMap<>(); + fieldValues.put("transaction_id.keyword", transactionId); + SearchResponse responseTrackerSearchResponse = elasticsearchService.exactSearch("response_tracker", fieldValues); + if (responseTrackerSearchResponse.getHits().getHits().length > 0) { + log.info("response: {}", responseTrackerSearchResponse.getHits().getHits()[0].getSourceAsString()); + String responseTrackerDtoString = responseTrackerSearchResponse.getHits().getHits()[0].getSourceAsString(); + Map resultMap = objectMapper.readValue(responseTrackerDtoString, new TypeReference>() { + }); + + String osid = resultMap.get(DcConstants.OSID).toString().substring(2); + ResponseTrackerDto responseTrackerDto = objectMapper.readerFor(ResponseTrackerDto.class). + readValue(responseTrackerDtoString); + String cacheKey = responseTrackerDto.getRegistryType() + "-" + transactionId; + ResponseMessageDTO responseMessageDTO = objectMapper.convertValue(txnStatusResponseDTO.getTxnStatus(), ResponseMessageDTO.class); + for (SearchResponseDTO searchResponseDTO : responseMessageDTO.getSearchResponse()) { + Map dataFieldValues = new HashMap<>(); + dataFieldValues.put("reference_id.keyword", searchResponseDTO.getReferenceId()); + SearchResponse responseDataSearchResponse = elasticsearchService.exactSearch("response_data", dataFieldValues); + if (responseDataSearchResponse.getHits().getHits().length > 0) { + String responseDataDtoString = responseDataSearchResponse.getHits().getHits()[0].getSourceAsString(); + Map responseDataResultMap = objectMapper.readValue(responseDataDtoString, new TypeReference>() { + }); + String responseDataOsid = responseDataResultMap.get(DcConstants.OSID).toString().substring(2); + ResponseDataDto responseDataDto = objectMapper.readerFor(ResponseDataDto.class). + readValue(responseDataDtoString); + + responseDataDto.setStatus(searchResponseDTO.getStatus()); + responseDataDto.setStatusReasonCode(searchResponseDTO.getStatusReasonCode()); + responseDataDto.setStatusReasonMessage(searchResponseDTO.getStatusReasonMessage()); + responseDataDto.setRegSubType(searchResponseDTO.getData().getRegSubType()); + responseDataDto.setRegRecordType(searchResponseDTO.getData().getRegRecordType()); + responseDataDto.setRegRecords(objectMapper.writeValueAsString(searchResponseDTO.getData().getRegRecords())); + responseDataDto.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + String responseDataString = objectMapper.writeValueAsString(responseDataDto); + HttpResponse responseD = Unirest.put(responseDataURL + "/" + responseDataOsid) + .header("Content-Type", "application/json") + .body(responseDataString) + .asJson(); + if (responseD.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseD.getBody().toString()); + return g2pcError; + } + } + } + responseTrackerDto.setStatus(headerDTO.getStatus()); + responseTrackerDto.setStatusReasonCode(headerDTO.getStatusReasonCode()); + responseTrackerDto.setStatusReasonMessage(headerDTO.getStatusReasonMessage()); + responseTrackerDto.setTotalCount(headerDTO.getTotalCount()); + responseTrackerDto.setCompletedCount(headerDTO.getCompletedCount()); + responseTrackerDto.setCorrelationId(statusResponseMessageDTO.getCorrelationId()); + responseTrackerDto.setMeta(objectMapper.writeValueAsString(headerDTO.getMeta())); + responseTrackerDto.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + String responseTrackerString = objectMapper.writeValueAsString(responseTrackerDto); + HttpResponse responseT = Unirest.put(responseTrackerURL + "/" + osid) + .header("Content-Type", "application/json") + .body(responseTrackerString) + .asJson(); + if (responseT.getStatus() != 200) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), responseT.getBody().toString()); + return g2pcError; + } + responseHandlerService.updateCache(cacheKey); + } + return new G2pcError(HttpStatus.OK.toString(), "Successfully stored in db"); + } +} diff --git a/g2pc-dc-core-lib/src/main/resources/application.yml b/g2pc-dc-core-lib/src/main/resources/application.yml new file mode 100644 index 0000000..9fc6158 --- /dev/null +++ b/g2pc-dc-core-lib/src/main/resources/application.yml @@ -0,0 +1,79 @@ +spring: + mvc: + pathmatch: + matching-strategy: ANT_PATH_MATCHER + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + +spring.data.redis: + repositories.enabled: false + host: localhost + password: 123456789 + port: 6376 + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dp1: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + + dp2: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + enabled: false + elasticsearch: + host: not_set + port: not_set + scheme: not_set diff --git a/g2pc-dc-core-lib/src/test/java/g2pc/dc/core/lib/G2pcDcCoreLibraryApplicationTests.java b/g2pc-dc-core-lib/src/test/java/g2pc/dc/core/lib/G2pcDcCoreLibraryApplicationTests.java new file mode 100644 index 0000000..80d6b83 --- /dev/null +++ b/g2pc-dc-core-lib/src/test/java/g2pc/dc/core/lib/G2pcDcCoreLibraryApplicationTests.java @@ -0,0 +1,17 @@ +package g2pc.dc.core.lib; + +import g2pc.dc.core.lib.service.RequestBuilderService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class G2pcDcCoreLibraryApplicationTests { + + @Autowired + RequestBuilderService requestBuilderService; + + @Test + void contextLoads() { + } +} diff --git a/g2pc-dp-core-lib/.gitignore b/g2pc-dp-core-lib/.gitignore new file mode 100644 index 0000000..e23d88f --- /dev/null +++ b/g2pc-dp-core-lib/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +mvnw +mvnw.cmd +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +.idea/ + diff --git a/g2pc-dp-core-lib/README.md b/g2pc-dp-core-lib/README.md new file mode 100644 index 0000000..e6b8cdb --- /dev/null +++ b/g2pc-dp-core-lib/README.md @@ -0,0 +1,25 @@ +# G2pc DP Core Lib + +## Overview +### Json Schema validations +* In this project Json schema input stream return by parent g2p-core lib. +* In RequestHandlerServiceImpl class input stream is called and will validate the Request DTO header and message +* If any thing doesn't match with the json schema exception handling is written for same. +* Below are some reference code for same. + +```` + InputStream schemaStream = commonUtils.getRequestMessageString(); + JsonNode jsonNodeMessage = objectMapper.readTree(messageString); + JsonSchema schemaMessage = null; + if(schemaStream !=null){ + schemaMessage = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4). + getSchema(schemaStream); + } + Set errorMessage = schemaMessage.validate(jsonNodeMessage); + List errorcombinedMessage= new ArrayList<>(); + for (ValidationMessage error : errorMessage){ + log.info("Validation errors" + error ); + errorcombinedMessage.add(new G2pcError("",error.getMessage())); + + } +```` \ No newline at end of file diff --git a/g2pc-dp-core-lib/pom.xml b/g2pc-dp-core-lib/pom.xml new file mode 100644 index 0000000..232de7e --- /dev/null +++ b/g2pc-dp-core-lib/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.0.12 + + + g2pc.dp.core.lib + g2pc-dp-core-library + 0.0.1-SNAPSHOT + g2pc-dp-core-library + g2pc-dp-core-library + + 17 + + + + org.springframework.boot + spring-boot-starter + + + + org.projectlombok + lombok + true + + + g2pc.core.lib + g2pc-core-library + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.postgresql + postgresql + 42.5.4 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + redis.clients + jedis + 4.4.3 + jar + + + org.springframework.boot + spring-boot-starter-data-redis + + + com.networknt + json-schema-validator + 1.0.57 + + + diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/G2pcDpCoreLibraryApplication.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/G2pcDpCoreLibraryApplication.java new file mode 100644 index 0000000..21fedb7 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/G2pcDpCoreLibraryApplication.java @@ -0,0 +1,16 @@ +package g2pc.dp.core.lib; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan({"g2pc.core.lib","g2pc.dp.core.lib","g2pc.dp.core.lib.serviceimpl", + "g2pc.dp.core.lib.repository"}) +public class G2pcDpCoreLibraryApplication { + + public static void main(String[] args) { + SpringApplication.run(G2pcDpCoreLibraryApplication.class, args); + } + +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/constants/DpConstants.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/constants/DpConstants.java new file mode 100644 index 0000000..d4669c2 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/constants/DpConstants.java @@ -0,0 +1,13 @@ +package g2pc.dp.core.lib.constants; + +public class DpConstants { + + private DpConstants() { + } + + public static final String SEARCH_REQUEST_RECEIVED = "Search request received"; + + public static final String RECORD_NOT_FOUND = "record_not_found"; + + public static final String CACHE_KEY_SEARCH_STRING = "request-farmer*"; +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/entity/MsgTrackerEntity.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/entity/MsgTrackerEntity.java new file mode 100644 index 0000000..466da4c --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/entity/MsgTrackerEntity.java @@ -0,0 +1,85 @@ +package g2pc.dp.core.lib.entity; + +import jakarta.persistence.*; +import lombok.*; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "msg_tracker") +public class MsgTrackerEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "version") + private String version; + + @Column(name = "message_id") + private String messageId; + + @Column(name = "message_ts") + private String messageTs; + + @Column(name = "action") + private String action; + + @Column(name = "status") + private String status; + + @Column(name = "status_reason_code") + private String statusReasonCode; + + @Column(name = "status_reason_message") + private String statusReasonMessage; + + @Column(name = "total_count") + private Integer totalCount; + + @Column(name = "completed_count") + private Integer completedCount; + + @Column(name = "sender_id") + private String senderId; + + @Column(name = "receiver_id") + private String receiverId; + + @Column(name = "is_msg_encrypted") + private Boolean isMsgEncrypted; + + @Column(name = "meta") + private String meta; + + @Column(name = "transaction_id") + private String transactionId; + + @Column(name = "correlation_id") + private String correlationId; + + @Column(name = "locale") + private String locale; + + @Column(name = "raw_message") + private String rawMessage; + + @Column(name = "protocol") + private String protocol; + + @Column(insertable = false, updatable = false) + private Timestamp createdDate; + + @Column(insertable = false) + private Timestamp lastUpdatedDate; + + @ToString.Exclude + @OneToMany(mappedBy = "msgTrackerEntity", cascade = CascadeType.ALL) + private List txnTrackerEntityList = new ArrayList<>(); +} \ No newline at end of file diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/entity/TxnTrackerEntity.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/entity/TxnTrackerEntity.java new file mode 100644 index 0000000..0047363 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/entity/TxnTrackerEntity.java @@ -0,0 +1,77 @@ +package g2pc.dp.core.lib.entity; + +import jakarta.persistence.*; +import lombok.*; +import java.sql.Timestamp; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "txn_tracker") +public class TxnTrackerEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "reference_id") + private String referenceId; + + @Column(name = "consent") + private Boolean consent; + + @Column(name = "authorize") + private Boolean authorize; + + @Column(name = "timestamp") + private String timestamp; + + @Column(name = "status") + private String status; + + @Column(name = "status_reason_code") + private String statusReasonCode; + + @Column(name = "status_reason_message") + private String statusReasonMessage; + + @Column(name = "version") + private String version; + + @Column(name = "reg_type") + private String regType; + + @Column(name = "reg_sub_type") + private String regSubType; + + @Column(name = "query_type") + private String queryType; + + @Column(name = "query") + private String query; + + @Column(name = "reg_record_type") + private String regRecordType; + + @Column(name = "no_of_records") + private Integer noOfRecords; + + @Column(name = "txn_type") + private String txnType; + + @Column(name = "txn_status") + private String txnStatus; + + @Column(insertable = false, updatable = false) + private Timestamp createdDate; + + @Column(insertable = false) + private Timestamp lastUpdatedDate; + + @ManyToOne(targetEntity = MsgTrackerEntity.class, cascade = CascadeType.ALL) + @JoinColumn(name = "msg_tracker_id", referencedColumnName = "id") + private MsgTrackerEntity msgTrackerEntity; +} \ No newline at end of file diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/repository/MsgTrackerRepository.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/repository/MsgTrackerRepository.java new file mode 100644 index 0000000..df89647 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/repository/MsgTrackerRepository.java @@ -0,0 +1,12 @@ +package g2pc.dp.core.lib.repository; + +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.Optional; + +@Repository +public interface MsgTrackerRepository extends JpaRepository { + + Optional findByTransactionId(String transactionId); +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/repository/TxnTrackerRepository.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/repository/TxnTrackerRepository.java new file mode 100644 index 0000000..5e0507c --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/repository/TxnTrackerRepository.java @@ -0,0 +1,13 @@ +package g2pc.dp.core.lib.repository; + +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import g2pc.dp.core.lib.entity.TxnTrackerEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.Optional; + +@Repository +public interface TxnTrackerRepository extends JpaRepository { + + Optional findByMsgTrackerEntity(MsgTrackerEntity msgTrackerEntity); +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/RequestHandlerService.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/RequestHandlerService.java new file mode 100644 index 0000000..5942f15 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/RequestHandlerService.java @@ -0,0 +1,14 @@ +package g2pc.dp.core.lib.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.common.AcknowledgementDTO; + +public interface RequestHandlerService { + + AcknowledgementDTO buildCacheRequest(String requestData, String cacheKey, String protocol, Boolean sunbirdEnabled) throws Exception; + + + public AcknowledgementDTO buildCacheStatusRequest(String statusRequestData, String cacheKey,String protocol) throws JsonProcessingException; + + +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/ResponseBuilderService.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/ResponseBuilderService.java new file mode 100644 index 0000000..181f289 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/ResponseBuilderService.java @@ -0,0 +1,46 @@ +package g2pc.dp.core.lib.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.common.security.TokenExpiryDto; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.response.SearchResponseDTO; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseMessageDTO; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import kong.unirest.UnirestException; +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.util.List; + +public interface ResponseBuilderService { + + ResponseMessageDTO buildResponseMessage(String transactionId, List searchResponseDTOList); + + ResponseHeaderDTO getResponseHeaderDTO(MsgTrackerEntity msgTrackerEntity); + + String buildResponseString(String signatureString, ResponseHeaderDTO responseHeaderDTO, ResponseMessageDTO messageDTO) throws JsonProcessingException; + + G2pcError sendOnSearchResponse(String responseString, String uri, String clientId, String clientSecret, String keyClockClientTokenUrl , InputStream fis , String txnType) throws Exception; + + public void saveToken(String cacheKey, TokenExpiryDto tokenExpiryDto) throws JsonProcessingException; + + public TokenExpiryDto getTokenFromCache(String clientId) throws JsonProcessingException; + + public String getValidatedToken(String keyCloakUrl, String clientId, String clientSecret) throws IOException, UnirestException, ParseException; + + StatusResponseMessageDTO buildStatusResponseMessage(StatusRequestMessageDTO statusRequestMessageDTO); + + String buildStatusResponseString(String signatureString, ResponseHeaderDTO responseHeaderDTO, StatusResponseMessageDTO statusResponseMessageDTO) throws JsonProcessingException; + + G2pcError sendOnSearchResponseSftp(String responseString, InputStream fis , String txnType, SftpServerConfigDTO sftpServerConfigDTO) throws Exception; + + public G2pcError buildOnSearchScheduler(List refRecordsStringsList , CacheDTO cacheDTOO, Boolean sunbirdEnabled) throws Exception ; + + public G2pcError buildOnStatusScheduler(CacheDTO cacheDTO) throws Exception ; + + } diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/TxnTrackerDbService.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/TxnTrackerDbService.java new file mode 100644 index 0000000..e2ed67d --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/TxnTrackerDbService.java @@ -0,0 +1,37 @@ +package g2pc.dp.core.lib.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.response.DataDTO; +import g2pc.core.lib.dto.search.message.response.SearchResponseDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import g2pc.dp.core.lib.entity.TxnTrackerEntity; + +import java.io.IOException; +import java.util.List; + +public interface TxnTrackerDbService { + + MsgTrackerEntity saveRequestDetails(RequestDTO requestDTO, String protocol, Boolean sunbirdEnabled) throws JsonProcessingException; + + int getRecordCount(Object records); + + List getUpdatedSearchResponseList(RequestDTO requestDTO, + List refRecordsStringsList, + String protocol, + Boolean sunbirdEnabled) throws IOException; + + DataDTO buildData(String regRecordsString, TxnTrackerEntity txnTrackerEntity); + + SearchResponseDTO buildSearchResponse(TxnTrackerEntity txnTrackerEntity, DataDTO dataDTO); + + MsgTrackerEntity saveStatusRequestDetails(StatusRequestDTO statusRequestDTO) throws JsonProcessingException; + + void updateStatusResponseDetails(ResponseMessageDTO responseMessageDTO, String transactionId); + + void updateMessageTrackerStatusDb(String transactionId); + + +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/TxnTrackerRedisService.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/TxnTrackerRedisService.java new file mode 100644 index 0000000..55e1b2d --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/service/TxnTrackerRedisService.java @@ -0,0 +1,16 @@ +package g2pc.dp.core.lib.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import java.util.List; + +public interface TxnTrackerRedisService { + + void saveRequestDetails(CacheDTO cacheDTO, String cacheKey) throws JsonProcessingException; + + void updateRequestDetails(String cacheKey, String status, CacheDTO cacheDTO) throws JsonProcessingException; + + List getCacheKeys(String cacheKeySearchString); + + String getRequestData(String cacheKey) throws JsonProcessingException; +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/RequestHandlerServiceImpl.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/RequestHandlerServiceImpl.java new file mode 100644 index 0000000..9b34479 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/RequestHandlerServiceImpl.java @@ -0,0 +1,90 @@ +package g2pc.dp.core.lib.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dp.core.lib.constants.DpConstants; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.dp.core.lib.service.TxnTrackerDbService; +import g2pc.dp.core.lib.service.TxnTrackerRedisService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class RequestHandlerServiceImpl implements RequestHandlerService { + + @Autowired + private CommonUtils commonUtils; + + @Autowired + private TxnTrackerRedisService txnTrackerRedisService; + + @Autowired + private TxnTrackerDbService txnTrackerDbService; + + /** + * Build a request to save in Redis cache + * @param requestData requestData to be stored in redis cache + * @param cacheKey cacheKey for which data to be stored + * @return Acknowledgement + */ + @Override + public AcknowledgementDTO buildCacheRequest(String requestData, String cacheKey, String protocol, Boolean sunbirdEnabled) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + log.info("Request saved in cache with status pending"); + CacheDTO cacheDTO = new CacheDTO(); + cacheDTO.setData(requestData); + cacheDTO.setStatus(HeaderStatusENUM.PDNG.toValue()); + cacheDTO.setProtocol(protocol); + cacheDTO.setCreatedDate(CommonUtils.getCurrentTimeStamp()); + cacheDTO.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class).readValue(cacheDTO.getData()); + txnTrackerRedisService.saveRequestDetails(cacheDTO, cacheKey); + txnTrackerDbService.saveRequestDetails(requestDTO, protocol,sunbirdEnabled); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + acknowledgementDTO.setMessage(DpConstants.SEARCH_REQUEST_RECEIVED); + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + return acknowledgementDTO; + } + + + + /** + * @param statusRequestData request data to store + * @param cacheKey cacheKey for which data storing + * @param protocol protocol to store in cache + * @return AcknowledgementDTO + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public AcknowledgementDTO buildCacheStatusRequest(String statusRequestData, String cacheKey, String protocol) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + log.info("Request saved in cache with status pending"); + CacheDTO cacheDTO = new CacheDTO(); + cacheDTO.setData(statusRequestData); + cacheDTO.setStatus(HeaderStatusENUM.PDNG.toValue()); + cacheDTO.setProtocol(protocol); + cacheDTO.setCreatedDate(CommonUtils.getCurrentTimeStamp()); + cacheDTO.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + StatusRequestDTO requestDTO = objectMapper.readerFor(StatusRequestDTO.class).readValue(cacheDTO.getData()); + txnTrackerRedisService.saveRequestDetails(cacheDTO, cacheKey); + txnTrackerDbService.saveStatusRequestDetails(requestDTO); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + acknowledgementDTO.setMessage(DpConstants.SEARCH_REQUEST_RECEIVED); + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + + return acknowledgementDTO; + } + +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/ResponseBuilderServiceImpl.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/ResponseBuilderServiceImpl.java new file mode 100644 index 0000000..6a5c927 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/ResponseBuilderServiceImpl.java @@ -0,0 +1,536 @@ +package g2pc.dp.core.lib.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.config.G2pUnirestHelper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.constants.G2pSecurityConstants; +import g2pc.core.lib.constants.SftpConstants; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.MetaDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.common.security.G2pTokenResponse; +import g2pc.core.lib.dto.common.security.TokenExpiryDto; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.response.DataDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.search.message.response.SearchResponseDTO; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseMessageDTO; +import g2pc.core.lib.dto.status.message.response.TxnStatusResponseDTO; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.enums.StatusTransactionTypeEnum; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.security.service.AsymmetricSignatureService; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import g2pc.core.lib.security.service.G2pTokenService; +import g2pc.core.lib.service.SftpHandlerService; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dp.core.lib.constants.DpConstants; +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import g2pc.dp.core.lib.entity.TxnTrackerEntity; +import g2pc.dp.core.lib.repository.MsgTrackerRepository; +import g2pc.dp.core.lib.repository.TxnTrackerRepository; +import g2pc.dp.core.lib.service.ResponseBuilderService; +import g2pc.dp.core.lib.service.TxnTrackerDbService; +import g2pc.dp.core.lib.service.TxnTrackerRedisService; +import g2pc.dp.core.lib.utils.DpCommonUtils; +import kong.unirest.HttpResponse; +import kong.unirest.UnirestException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.*; + +@Service +@Slf4j +public class ResponseBuilderServiceImpl implements ResponseBuilderService { + + @Autowired + private RedisTemplate redisTemplate; + + @Value("${crypto.to_dc.support_encryption}") + private boolean isEncrypt; + + @Value("${crypto.to_dc.support_signature}") + private boolean isSign; + + @Value("${crypto.to_dc.password}") + private String p12Password; + + @Value("${crypto.to_dc.id}") + private String dpId; + + @Value("${crypto.to_dc.key_path}") + private String farmerKeyPath; + + @Value("${client.api_urls.client_search_api}") + String onSearchURL; + + @Value("${client.api_urls.client_status_api}") + String onStatusURL; + + @Value("${keycloak.from-dc.client-id}") + private String dcClientId; + + @Value("${keycloak.from-dc.client-secret}") + private String dcClientSecret; + + @Value("${keycloak.from-dc.url}") + private String keyClockClientTokenUrl; + + @Autowired + G2pUnirestHelper g2pUnirestHelper; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Autowired + G2pTokenService g2pTokenService; + + @Autowired + AsymmetricSignatureService asymmetricSignatureService; + + @Autowired + MsgTrackerRepository msgTrackerRepository; + + @Autowired + TxnTrackerRepository txnTrackerRepository; + + @Autowired + TxnTrackerDbService txnTrackerDbService; + + @Autowired + private SftpHandlerService sftpHandlerService; + + + @Autowired + private TxnTrackerRedisService txnTrackerRedisService; + + + @Autowired + private ResourceLoader resourceLoader; + + @Autowired + private DpCommonUtils dpCommonUtils; + + /** + * Get response header + * + * @param msgTrackerEntity msgTrackerEntity used to create ResponseHeaderFto + * @return ResponseHeaderDTO + */ + @Override + public ResponseHeaderDTO getResponseHeaderDTO(MsgTrackerEntity msgTrackerEntity) { + ResponseHeaderDTO headerDTO = new ResponseHeaderDTO(); + headerDTO.setVersion(msgTrackerEntity.getVersion()); + headerDTO.setMessageId(msgTrackerEntity.getMessageId()); + headerDTO.setMessageTs(msgTrackerEntity.getMessageTs()); + headerDTO.setAction(msgTrackerEntity.getAction()); + headerDTO.setSenderId(msgTrackerEntity.getSenderId()); + headerDTO.setReceiverId(msgTrackerEntity.getReceiverId()); + headerDTO.setIsMsgEncrypted(msgTrackerEntity.getIsMsgEncrypted()); + headerDTO.setStatus(msgTrackerEntity.getStatus()); + headerDTO.setStatusReasonCode(msgTrackerEntity.getStatusReasonCode()); + headerDTO.setStatusReasonMessage(msgTrackerEntity.getStatusReasonMessage()); + headerDTO.setTotalCount(msgTrackerEntity.getTotalCount()); + headerDTO.setCompletedCount(msgTrackerEntity.getCompletedCount()); + Map metaMap = new HashMap<>(); + MetaDTO metaDTO = new MetaDTO(); + metaDTO.setData(metaMap); + headerDTO.setMeta(metaDTO); + return headerDTO; + } + + /** + * Build a response message + * + * @param transactionId transactionId to build response message + * @param searchResponseDTOList list of searchResponseDto to store in response message + * @return ResponseMessageDTO + */ + @Override + public ResponseMessageDTO buildResponseMessage(String transactionId, List searchResponseDTOList) { + ResponseMessageDTO messageDTO = new ResponseMessageDTO(); + messageDTO.setTransactionId(transactionId); + messageDTO.setCorrelationId(CommonUtils.generateUniqueId("C")); + messageDTO.setSearchResponse(searchResponseDTOList); + return messageDTO; + } + + /** + * Build a response string + * + * @param signatureString signature to store + * @param responseHeaderDTO response header + * @param messageDTO message + * @return response String + */ + @Override + public String buildResponseString(String signatureString, ResponseHeaderDTO responseHeaderDTO, + ResponseMessageDTO messageDTO) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + ResponseDTO responseDTO = new ResponseDTO(); + responseDTO.setSignature(signatureString); + responseDTO.setHeader(responseHeaderDTO); + responseDTO.setMessage(messageDTO); + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(responseDTO); + } + + /** + * @param responseString response string to store + * @param uri endpoint for transaction + * @param clientId keycloak clientId + * @param clientSecret keycloak clientSecret + * @param keyClockClientTokenUrl keycloak ClientTokenUrl + * @param fis .p12 file input stream + * @param txnType txnType + * @return G2pcError + * @throws Exception + */ + @Override + public G2pcError sendOnSearchResponse(String responseString, String uri, String clientId, String clientSecret, String keyClockClientTokenUrl + , InputStream fis, String txnType) throws Exception { + log.info("Send on-search response"); + log.info("Is encrypted ? -> " + isEncrypt); + log.info("Is signed ? -> " + isSign); + responseString = createSignature(isEncrypt, isSign, responseString, fis, txnType); + String jwtToken = getValidatedToken(keyClockClientTokenUrl, clientId, clientSecret); + HttpResponse response = g2pUnirestHelper.g2pPost(uri) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer " + jwtToken) + .body(responseString) + .asString(); + log.info(txnType + "Response status = {}", response.getStatus()); + if (response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR.value()) { + return new G2pcError(HttpStatus.INTERNAL_SERVER_ERROR.toString(), response.getBody()); + } else if (response.getStatus() == HttpStatus.UNAUTHORIZED.value()) { + return new G2pcError(HttpStatus.UNAUTHORIZED.toString(), response.getBody()); + } else if (response.getStatus() == HttpStatus.BAD_REQUEST.value()) { + return new G2pcError(HttpStatus.BAD_REQUEST.toString(), response.getBody()); + } else if (response.getStatus() != HttpStatus.OK.value()) { + return new G2pcError("err.service.unavailable", response.getBody()); + } + return new G2pcError(HttpStatus.OK.toString(), response.getBody()); + } + + /** + * Method to store token in cache + * + * @param cacheKey cacheKey cache key for which data is storing + * @param tokenExpiryDto token expiry dto + * @throws JsonProcessingException might be thrown + */ + @Override + public void saveToken(String cacheKey, TokenExpiryDto tokenExpiryDto) throws JsonProcessingException { + ValueOperations val = redisTemplate.opsForValue(); + val.set(cacheKey, new ObjectMapper().writeValueAsString(tokenExpiryDto)); + } + + /** + * Method to get token stored in cache + * + * @param clientId client Id + * @return TokenExpiryDto tokenExpiryDto + * @throws JsonProcessingException might be thrown + */ + @Override + public TokenExpiryDto getTokenFromCache(String clientId) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + Set redisKeys = this.redisTemplate.keys(clientId); + List cacheKeysList = new ArrayList((Collection) Objects.requireNonNull(redisKeys)); + if (!cacheKeysList.isEmpty()) { + String cacheKey = cacheKeysList.get(0); + String tokenData = (String) this.redisTemplate.opsForValue().get(cacheKey); + TokenExpiryDto tokenExpiryDto = objectMapper.readerFor(TokenExpiryDto.class).readValue(tokenData); + return tokenExpiryDto; + } + return null; + } + + /** + * The method to get validated token + * + * @param keyCloakUrl keycloak url to validate token + * @param clientId client id + * @param clientSecret client secret + * @return String validated token + * @throws IOException IOException might be thrown + * @throws ParseException ParseException might be thrown + * @throws UnirestException UnirestException might be thrown + */ + @Override + public String getValidatedToken(String keyCloakUrl, String clientId, String clientSecret) throws IOException, ParseException, UnirestException { + TokenExpiryDto tokenExpiryDto = getTokenFromCache(clientId); + + String jwtToken = ""; + if (g2pTokenService.isTokenExpired(tokenExpiryDto)) { + G2pTokenResponse tokenResponse = g2pTokenService.getToken(keyCloakUrl, clientId, clientSecret); + jwtToken = tokenResponse.getAccessToken(); + saveToken(clientId, g2pTokenService.createTokenExpiryDto(tokenResponse)); + } else { + jwtToken = tokenExpiryDto.getToken(); + } + return jwtToken; + } + + /** + * The method is to create signature + * + * @param isSign signature flag + * @param isEncrypt encryption flag + * @param responseString response string + * @return String signature + * @throws Exception Exception might be thrown + */ + @SuppressWarnings("unchecked") + private String createSignature(boolean isEncrypt, boolean isSign, String responseString + , InputStream fis, String txnType) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + ResponseDTO responseDTO = objectMapper.readerFor(ResponseDTO.class). + readValue(responseString); + byte[] json = objectMapper.writeValueAsBytes(responseDTO.getMessage()); + ResponseHeaderDTO responseHeaderDTO = (ResponseHeaderDTO) responseDTO.getHeader(); + String responseHeaderString = objectMapper.writeValueAsString(responseHeaderDTO); + String signature = null; + String messageString = ""; + if (txnType.equals(CoreConstants.DP_STATUS_URL)) { + StatusResponseMessageDTO messageDTO = objectMapper.readValue(json, StatusResponseMessageDTO.class); + messageString = objectMapper.writeValueAsString(messageDTO); + } else { + ResponseMessageDTO messageDTO = objectMapper.readValue(json, ResponseMessageDTO.class); + messageString = objectMapper.writeValueAsString(messageDTO); + } + + if (isSign) { + if (isEncrypt) { + String encryptedMessageString = encryptDecrypt.g2pEncrypt(messageString, G2pSecurityConstants.SECRET_KEY); + responseDTO.setMessage(encryptedMessageString); + responseDTO.getHeader().setIsMsgEncrypted(true); + Map meta = (Map) responseDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, true); + responseDTO.getHeader().getMeta().setData(meta); + responseHeaderString = objectMapper.writeValueAsString(responseDTO.getHeader()); + byte[] asymmetricSignature = asymmetricSignatureService.sign(responseHeaderString + encryptedMessageString, fis, p12Password); + signature = Base64.getEncoder().encodeToString(asymmetricSignature); + log.info("Encrypted message ->" + encryptedMessageString); + log.info("Hashed Signature ->" + signature); + } else { + responseDTO.getHeader().setIsMsgEncrypted(false); + Map meta = (Map) responseDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, true); + responseDTO.getHeader().getMeta().setData(meta); + responseHeaderString = objectMapper.writeValueAsString(responseDTO.getHeader()); + byte[] asymmetricSignature = asymmetricSignatureService.sign(responseHeaderString + messageString, fis, p12Password); + signature = Base64.getEncoder().encodeToString(asymmetricSignature); + log.info("Hashed Signature ->" + signature); + } + } else { + if (isEncrypt) { + String encryptedMessageString = encryptDecrypt.g2pEncrypt(messageString, G2pSecurityConstants.SECRET_KEY); + responseDTO.setMessage(encryptedMessageString); + responseDTO.getHeader().setIsMsgEncrypted(true); + Map meta = (Map) responseDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, false); + responseDTO.getHeader().getMeta().setData(meta); + log.info("Encrypted message ->" + encryptedMessageString); + } else { + responseDTO.getHeader().setIsMsgEncrypted(false); + Map meta = (Map) responseDTO.getHeader().getMeta().getData(); + meta.put(CoreConstants.IS_SIGN, false); + responseDTO.getHeader().getMeta().setData(meta); + } + } + responseDTO.setSignature(signature); + responseString = objectMapper.writeValueAsString(responseDTO); + return responseString; + } + + /** + * @param statusRequestMessageDTO status request message dto + * @return StatusResponseMessageDTO + */ + @Override + public StatusResponseMessageDTO buildStatusResponseMessage(StatusRequestMessageDTO statusRequestMessageDTO) { + StatusResponseMessageDTO statusResponseMessageDTO = new StatusResponseMessageDTO(); + statusResponseMessageDTO.setTransactionId(statusRequestMessageDTO.getTransactionId()); + statusResponseMessageDTO.setCorrelationId(CommonUtils.generateUniqueId("C")); + String searchTransactionId = (String) statusRequestMessageDTO.getTxnStatusRequest().getAttributeValue(); + Optional msgTrackerEntityOptional = msgTrackerRepository.findByTransactionId(searchTransactionId); + MsgTrackerEntity msgTrackerEntity = msgTrackerEntityOptional.get(); + List txnTrackerEntityList = msgTrackerEntity.getTxnTrackerEntityList(); + TxnStatusResponseDTO txnStatusResponseDTO = new TxnStatusResponseDTO(); + + if (statusRequestMessageDTO.getTxnStatusRequest().getTxnType().equals(StatusTransactionTypeEnum.SEARCH.toValue())) { + txnStatusResponseDTO.setTxnType(StatusTransactionTypeEnum.ON_SUBSCRIBE.toValue()); + ResponseMessageDTO responseMessageDTO = new ResponseMessageDTO(); + responseMessageDTO.setTransactionId(msgTrackerEntity.getTransactionId()); + responseMessageDTO.setCorrelationId(msgTrackerEntity.getCorrelationId()); + List searchResponse = new ArrayList<>(); + for (TxnTrackerEntity txnTrackerEntity : txnTrackerEntityList) { + + SearchResponseDTO searchResponseDTO = new SearchResponseDTO(); + searchResponseDTO.setReferenceId(txnTrackerEntity.getReferenceId()); + searchResponseDTO.setTimestamp(txnTrackerEntity.getTimestamp()); + searchResponseDTO.setStatus(txnTrackerEntity.getStatus()); + searchResponseDTO.setStatusReasonCode(txnTrackerEntity.getStatusReasonCode()); + searchResponseDTO.setStatusReasonMessage(txnTrackerEntity.getStatusReasonMessage()); + DataDTO dataDTO = new DataDTO(); + dataDTO.setVersion(txnTrackerEntity.getVersion()); + dataDTO.setRegType(txnTrackerEntity.getRegType()); + dataDTO.setRegSubType(txnTrackerEntity.getRegSubType()); + dataDTO.setRegRecordType(txnTrackerEntity.getRegRecordType()); + searchResponseDTO.setData(dataDTO); + searchResponse.add(searchResponseDTO); + } + responseMessageDTO.setSearchResponse(searchResponse); + txnStatusResponseDTO.setTxnStatus(responseMessageDTO); + txnTrackerDbService.updateStatusResponseDetails(responseMessageDTO, statusResponseMessageDTO.getTransactionId()); + } + + statusResponseMessageDTO.setTxnStatusResponse(txnStatusResponseDTO); + + return statusResponseMessageDTO; + } + + /** + * @param signatureString signature to be added in request + * @param responseHeaderDTO header dto ti be added in request + * @param statusResponseMessageDTO response message dto to be added + * @return response dto + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public String buildStatusResponseString(String signatureString, ResponseHeaderDTO responseHeaderDTO, StatusResponseMessageDTO statusResponseMessageDTO) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + StatusResponseDTO statusResponseDTO = new StatusResponseDTO(); + statusResponseDTO.setSignature(signatureString); + statusResponseDTO.setHeader(responseHeaderDTO); + statusResponseDTO.setMessage(statusResponseMessageDTO); + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(statusResponseDTO); + } + + /** + * The method is to send on search response using sftp + * + * @param responseString required + * @return String + * @throws Exception jsonProcessingException might be thrown + */ + @Override + public G2pcError sendOnSearchResponseSftp(String responseString, InputStream fis, + String txnType, SftpServerConfigDTO sftpServerConfigDTO) throws Exception { + log.info("Send on-search response"); + log.info("Is encrypted ? -> " + isEncrypt); + log.info("Is signed ? -> " + isSign); + responseString = createSignature(isEncrypt, isSign, responseString, fis, txnType); + + String originalFilename = UUID.randomUUID() + ".json"; + Path tempFile = Paths.get(System.getProperty("java.io.tmpdir"), originalFilename); + Files.createFile(tempFile); + Files.write(tempFile, responseString.getBytes()); + + Boolean status = sftpHandlerService.uploadFileToSftp(sftpServerConfigDTO, tempFile.toString(), + sftpServerConfigDTO.getRemoteOutboundDirectory()); + Files.delete(tempFile); + G2pcError g2pcError; + if (Boolean.FALSE.equals(status)) { + g2pcError = new G2pcError(ExceptionsENUM.ERROR_SERVICE_UNAVAILABLE.toValue(), SftpConstants.UPLOAD_ERROR_MESSAGE); + log.error(SftpConstants.UPLOAD_ERROR_MESSAGE); + } else { + g2pcError = new G2pcError(HttpStatus.OK.toString(), SftpConstants.UPLOAD_SUCCESS_MESSAGE); + } + return g2pcError; + } + + @Override + public G2pcError buildOnSearchScheduler(List refRecordsStringsList, CacheDTO cacheDTO, Boolean sunbirdEnabled) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + G2pcError g2pcError = new G2pcError(); + String protocol = cacheDTO.getProtocol(); + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class).readValue(cacheDTO.getData()); + RequestMessageDTO messageDTO = objectMapper.convertValue(requestDTO.getMessage(), RequestMessageDTO.class); + String transactionId = messageDTO.getTransactionId(); + + MsgTrackerEntity msgTrackerEntity = txnTrackerDbService.saveRequestDetails(requestDTO, protocol, sunbirdEnabled); + List searchResponseDTOList = txnTrackerDbService.getUpdatedSearchResponseList( + requestDTO, refRecordsStringsList, protocol, sunbirdEnabled); + ResponseHeaderDTO headerDTO = getResponseHeaderDTO(msgTrackerEntity); + ResponseMessageDTO responseMessageDTO = buildResponseMessage(transactionId, searchResponseDTOList); + Map meta = (Map) headerDTO.getMeta().getData(); + meta.put(CoreConstants.DP_ID, dpId); + requestDTO.getHeader().getMeta().setData(meta); + String responseString = buildResponseString("signature", + headerDTO, responseMessageDTO); + responseString = CommonUtils.formatString(responseString); + log.info("on-search response = {}", responseString); + Resource resource = resourceLoader.getResource(farmerKeyPath); + InputStream fis = resource.getInputStream(); + + if (protocol.equals(CoreConstants.SEND_PROTOCOL_HTTPS)) { + g2pcError = sendOnSearchResponse(responseString, onSearchURL, dcClientId, + dcClientSecret, keyClockClientTokenUrl, fis, CoreConstants.SEARCH_TXN_TYPE); + } else if (protocol.equals(CoreConstants.SEND_PROTOCOL_SFTP)) { + SftpServerConfigDTO sftpServerConfigDTO = dpCommonUtils.getSftpConfigForDp(); + g2pcError = sendOnSearchResponseSftp(responseString, fis, + CoreConstants.SEARCH_TXN_TYPE, sftpServerConfigDTO); + } + if (!Objects.requireNonNull(g2pcError).getCode().equals(HttpStatus.OK.toString())) { + throw new G2pHttpException(g2pcError); + } + + + return g2pcError; + } + + @Override + public G2pcError buildOnStatusScheduler(CacheDTO cacheDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + StatusRequestDTO statusRequestDTO = objectMapper.readerFor(StatusRequestDTO.class).readValue(cacheDTO.getData()); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.convertValue(statusRequestDTO.getMessage(), StatusRequestMessageDTO.class); + + MsgTrackerEntity msgTrackerEntity = txnTrackerDbService.saveStatusRequestDetails(statusRequestDTO); + ResponseHeaderDTO responseHeaderDTO = getResponseHeaderDTO(msgTrackerEntity); + + StatusResponseMessageDTO statusResponseMessageDTO = buildStatusResponseMessage(statusRequestMessageDTO); + + Map meta = (Map) responseHeaderDTO.getMeta().getData(); + meta.put(CoreConstants.DP_ID, dpId); + statusRequestDTO.getHeader().getMeta().setData(meta); + + String statusResponseString = buildStatusResponseString("signature", responseHeaderDTO, statusResponseMessageDTO); + statusResponseString = CommonUtils.formatString(statusResponseString); + log.info("on-status response = {}", statusResponseString); + Resource resource = resourceLoader.getResource(farmerKeyPath); + InputStream fis = resource.getInputStream(); + return sendOnSearchResponse(statusResponseString, onStatusURL, dcClientId, dcClientSecret, keyClockClientTokenUrl, fis, CoreConstants.DP_STATUS_URL); + + } +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/TxnTrackerDbServiceImpl.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/TxnTrackerDbServiceImpl.java new file mode 100644 index 0000000..ac319ae --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/TxnTrackerDbServiceImpl.java @@ -0,0 +1,297 @@ +package g2pc.dp.core.lib.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.request.SearchRequestDTO; +import g2pc.core.lib.dto.search.message.response.DataDTO; +import g2pc.core.lib.dto.search.message.response.ResponsePaginationDTO; +import g2pc.core.lib.dto.search.message.response.SearchResponseDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.TxnStatusRequestDTO; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.enums.LocalesENUM; +import g2pc.core.lib.enums.QueryTypeEnum; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dp.core.lib.constants.DpConstants; +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import g2pc.dp.core.lib.entity.TxnTrackerEntity; +import g2pc.dp.core.lib.repository.MsgTrackerRepository; +import g2pc.dp.core.lib.repository.TxnTrackerRepository; +import g2pc.dp.core.lib.service.TxnTrackerDbService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +@Slf4j +public class TxnTrackerDbServiceImpl implements TxnTrackerDbService { + + @Autowired + private MsgTrackerRepository msgTrackerRepository; + + @Autowired + private TxnTrackerRepository txnTrackerRepository; + + /** + * Save request details + * + * @param requestDTO requestDTO to save in Db + * @return request details entity + */ + @Override + public MsgTrackerEntity saveRequestDetails(RequestDTO requestDTO, String protocol, Boolean sunbirdEnabled) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + + MsgTrackerEntity entity; + + HeaderDTO headerDTO = requestDTO.getHeader(); + RequestMessageDTO messageDTO = objectMapper.convertValue(requestDTO.getMessage(), RequestMessageDTO.class); + String transactionId = messageDTO.getTransactionId(); + Optional msgTrackerEntityOptional = msgTrackerRepository.findByTransactionId(transactionId); + if (msgTrackerEntityOptional.isEmpty()) { + MsgTrackerEntity msgTrackerEntity = new MsgTrackerEntity(); + msgTrackerEntity.setVersion(headerDTO.getVersion()); + msgTrackerEntity.setMessageId(headerDTO.getMessageId()); + msgTrackerEntity.setMessageTs(headerDTO.getMessageTs()); + msgTrackerEntity.setAction(headerDTO.getAction()); + msgTrackerEntity.setSenderId(headerDTO.getSenderId()); + msgTrackerEntity.setReceiverId(headerDTO.getReceiverId()); + msgTrackerEntity.setIsMsgEncrypted(headerDTO.getIsMsgEncrypted()); + msgTrackerEntity.setTransactionId(messageDTO.getTransactionId()); + msgTrackerEntity.setRawMessage(objectMapper.writeValueAsString(requestDTO)); + msgTrackerEntity.setProtocol(protocol); + + List searchRequestDTOList = messageDTO.getSearchRequest(); + for (SearchRequestDTO searchRequestDTO : searchRequestDTOList) { + TxnTrackerEntity txnTrackerEntity = new TxnTrackerEntity(); + txnTrackerEntity.setReferenceId(searchRequestDTO.getReferenceId()); + txnTrackerEntity.setTimestamp(searchRequestDTO.getTimestamp()); + txnTrackerEntity.setVersion(searchRequestDTO.getSearchCriteria().getVersion()); + txnTrackerEntity.setRegType(searchRequestDTO.getSearchCriteria().getRegType()); + txnTrackerEntity.setRegSubType(searchRequestDTO.getSearchCriteria().getRegSubType()); + txnTrackerEntity.setQueryType(searchRequestDTO.getSearchCriteria().getQueryType()); + txnTrackerEntity.setQuery(objectMapper.writeValueAsString(searchRequestDTO.getSearchCriteria().getQuery())); + txnTrackerEntity.setCreatedDate(new Timestamp(System.currentTimeMillis())); + txnTrackerEntity.setLastUpdatedDate(new Timestamp(System.currentTimeMillis())); + txnTrackerEntity.setMsgTrackerEntity(msgTrackerEntity); + msgTrackerEntity.getTxnTrackerEntityList().add(txnTrackerEntity); + } + entity = msgTrackerRepository.save(msgTrackerEntity); + } else { + entity = msgTrackerEntityOptional.get(); + } + return entity; + } + + /** + * Get record count + * + * @param records required + * @return record count + */ + @SuppressWarnings("unchecked") + @Override + public int getRecordCount(Object records) { + Map objectMap = new ObjectMapper().convertValue(records, Map.class); + return objectMap.size(); + } + + /** + * Build a search response + * + * @param txnTrackerEntity txnTrackerEntity to build search response + * @param dataDTO dataDTO to build search response dto + * @return SearchResponseDTO + */ + @Override + public SearchResponseDTO buildSearchResponse(TxnTrackerEntity txnTrackerEntity, DataDTO dataDTO) { + ResponsePaginationDTO paginationDTO = new ResponsePaginationDTO(); + paginationDTO.setPageSize(10); + paginationDTO.setPageNumber(1); + paginationDTO.setTotalCount(100); + + SearchResponseDTO searchResponseDTO = new SearchResponseDTO(); + searchResponseDTO.setReferenceId(txnTrackerEntity.getReferenceId()); + searchResponseDTO.setTimestamp(txnTrackerEntity.getTimestamp()); + searchResponseDTO.setStatus(txnTrackerEntity.getStatus()); + searchResponseDTO.setStatusReasonCode(txnTrackerEntity.getStatusReasonCode()); + searchResponseDTO.setStatusReasonMessage(txnTrackerEntity.getStatusReasonMessage()); + searchResponseDTO.setData(dataDTO); + searchResponseDTO.setPagination(paginationDTO); + searchResponseDTO.setLocale(LocalesENUM.EN.toValue()); + + return searchResponseDTO; + } + + /** + * Build data + * + * @param regRecordsString regRecord to store in data dto + * @return DataDTO + */ + @Override + public DataDTO buildData(String regRecordsString, TxnTrackerEntity txnTrackerEntity) { + DataDTO dataDTO = new DataDTO(); + dataDTO.setVersion(txnTrackerEntity.getVersion()); + dataDTO.setRegType(txnTrackerEntity.getRegType()); + dataDTO.setRegSubType(txnTrackerEntity.getRegSubType()); + dataDTO.setRegRecordType(txnTrackerEntity.getRegRecordType()); + if (StringUtils.isNotEmpty(regRecordsString)) { + dataDTO.setRegRecords(regRecordsString); + } else { + dataDTO.setRegRecords(null); + } + return dataDTO; + } + + /** + * Get updated search response list + * + * @param requestDTO request dto to be updated + * @param refRecordsStringsList list of records + * @return updated search response list + */ + @Override + public List getUpdatedSearchResponseList(RequestDTO requestDTO, + List refRecordsStringsList, + String protocol, + Boolean sunbirdEnabled) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + List searchResponseDTOList = new ArrayList<>(); + MsgTrackerEntity msgTrackerEntity = saveRequestDetails(requestDTO, protocol, sunbirdEnabled); + + List txnTrackerEntityList = msgTrackerEntity.getTxnTrackerEntityList(); + int totalCount = txnTrackerEntityList.size(); + int completedCount = 0; + int index = 0; + for (TxnTrackerEntity txnTrackerEntity : txnTrackerEntityList) { + String refRecordsString = refRecordsStringsList.get(index); + + txnTrackerEntity.setConsent(true); + txnTrackerEntity.setAuthorize(true); + txnTrackerEntity.setRegRecordType(QueryTypeEnum.NAMEDQUERY.toValue()); + DataDTO dataDTO = buildData(refRecordsString, txnTrackerEntity); + if (refRecordsString.isEmpty()) { + txnTrackerEntity.setStatus(HeaderStatusENUM.RJCT.toValue()); + txnTrackerEntity.setStatusReasonCode(DpConstants.RECORD_NOT_FOUND); + txnTrackerEntity.setStatusReasonMessage(DpConstants.RECORD_NOT_FOUND); + txnTrackerEntity.setNoOfRecords(0); + } else { + txnTrackerEntity.setStatus(HeaderStatusENUM.SUCC.toValue()); + txnTrackerEntity.setStatusReasonCode(HeaderStatusENUM.SUCC.toValue()); + txnTrackerEntity.setStatusReasonMessage(HeaderStatusENUM.SUCC.toValue()); + txnTrackerEntity.setNoOfRecords(1); + completedCount++; + } + searchResponseDTOList.add(buildSearchResponse(txnTrackerEntity, dataDTO)); + index++; + } + msgTrackerEntity.setTxnTrackerEntityList(txnTrackerEntityList); + msgTrackerEntity.setStatus(HeaderStatusENUM.SUCC.toValue()); + msgTrackerEntity.setStatusReasonCode(HeaderStatusENUM.SUCC.toValue()); + msgTrackerEntity.setStatusReasonMessage(HeaderStatusENUM.SUCC.toValue()); + msgTrackerEntity.setTotalCount(totalCount); + msgTrackerEntity.setCompletedCount(completedCount); + msgTrackerEntity.setCorrelationId(CommonUtils.generateUniqueId("C")); + msgTrackerEntity.setLocale(LocalesENUM.EN.toValue()); + msgTrackerRepository.save(msgTrackerEntity); + return searchResponseDTOList; + } + + /** + * @param statusRequestDTO statusRequestDTO to save in db + * @return MsgTrackerEntity + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public MsgTrackerEntity saveStatusRequestDetails(StatusRequestDTO statusRequestDTO) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + + MsgTrackerEntity entity; + + HeaderDTO headerDTO = statusRequestDTO.getHeader(); + StatusRequestMessageDTO messageDTO = objectMapper.convertValue(statusRequestDTO.getMessage(), StatusRequestMessageDTO.class); + String transactionId = messageDTO.getTransactionId(); + Optional msgTrackerEntityOptional = msgTrackerRepository.findByTransactionId(transactionId); + if (msgTrackerEntityOptional.isEmpty()) { + MsgTrackerEntity msgTrackerEntity = new MsgTrackerEntity(); + msgTrackerEntity.setVersion(headerDTO.getVersion()); + msgTrackerEntity.setMessageId(headerDTO.getMessageId()); + msgTrackerEntity.setMessageTs(headerDTO.getMessageTs()); + msgTrackerEntity.setAction(headerDTO.getAction()); + msgTrackerEntity.setSenderId(headerDTO.getSenderId()); + msgTrackerEntity.setReceiverId(headerDTO.getReceiverId()); + msgTrackerEntity.setIsMsgEncrypted(headerDTO.getIsMsgEncrypted()); + msgTrackerEntity.setTransactionId(messageDTO.getTransactionId()); + msgTrackerEntity.setRawMessage(objectMapper.writeValueAsString(statusRequestDTO)); + + TxnStatusRequestDTO txnStatusRequestDTO = messageDTO.getTxnStatusRequest(); + TxnTrackerEntity txnTrackerEntity = new TxnTrackerEntity(); + txnTrackerEntity.setTimestamp(null); + txnTrackerEntity.setVersion(null); + txnTrackerEntity.setRegType(null); + txnTrackerEntity.setRegSubType(null); + txnTrackerEntity.setQueryType(null); + txnTrackerEntity.setQuery(null); + txnTrackerEntity.setTxnStatus(null); + txnTrackerEntity.setTxnType(txnStatusRequestDTO.getTxnType()); + txnTrackerEntity.setCreatedDate(new Timestamp(System.currentTimeMillis())); + txnTrackerEntity.setLastUpdatedDate(new Timestamp(System.currentTimeMillis())); + txnTrackerEntity.setMsgTrackerEntity(msgTrackerEntity); + msgTrackerEntity.getTxnTrackerEntityList().add(txnTrackerEntity); + + entity = msgTrackerRepository.save(msgTrackerEntity); + } else { + entity = msgTrackerEntityOptional.get(); + } + return entity; + } + + /** + * @param responseMessageDTO responseMessageDTO to update in db + * @param transactionId transactionId to search in db + */ + @Override + public void updateStatusResponseDetails(ResponseMessageDTO responseMessageDTO, String transactionId) { + + Optional msgTrackerEntityOptional = msgTrackerRepository.findByTransactionId(transactionId); + MsgTrackerEntity msgTrackerEntity = msgTrackerEntityOptional.get(); + Optional trackerEntityOptional = txnTrackerRepository.findByMsgTrackerEntity(msgTrackerEntity); + TxnTrackerEntity txnTrackerEntity = trackerEntityOptional.get(); + txnTrackerEntity.setTxnStatus(responseMessageDTO.toString()); + txnTrackerRepository.save(txnTrackerEntity); + } + + /** + * @param transactionId transactionId used to search data + */ + @Override + public void updateMessageTrackerStatusDb(String transactionId) { + Optional msgTrackerEntityOptional = msgTrackerRepository.findByTransactionId(transactionId); + MsgTrackerEntity msgTrackerEntity = msgTrackerEntityOptional.get(); + msgTrackerEntity.setStatus(HeaderStatusENUM.SUCC.toValue()); + msgTrackerEntity.setStatusReasonCode(HeaderStatusENUM.SUCC.toValue()); + msgTrackerEntity.setStatusReasonMessage(HeaderStatusENUM.SUCC.toValue()); + msgTrackerEntity.setCorrelationId(CommonUtils.generateUniqueId("C")); + msgTrackerRepository.save(msgTrackerEntity); + } + +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/TxnTrackerRedisServiceImpl.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/TxnTrackerRedisServiceImpl.java new file mode 100644 index 0000000..434f5ef --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/serviceimpl/TxnTrackerRedisServiceImpl.java @@ -0,0 +1,76 @@ +package g2pc.dp.core.lib.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dp.core.lib.service.TxnTrackerRedisService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +@Service +@Slf4j +public class TxnTrackerRedisServiceImpl implements TxnTrackerRedisService { + + @Autowired + private RedisTemplate redisTemplate; + + /** + * Save a request in Redis cache + * @param cacheDTO cache dto to save in cache + * @param cacheKey cache key for which data is stored + */ + @Override + public void saveRequestDetails(CacheDTO cacheDTO, String cacheKey) throws JsonProcessingException { + ValueOperations val = redisTemplate.opsForValue(); + val.set(cacheKey, (new ObjectMapper()).writeValueAsString(cacheDTO)); + } + + /** + * Update a request in Redis cache after response + * + * @param cacheKey cache key for which data is stored + * @param status status to be + * @param cacheDTO cache dto to save in cache + */ + @Override + public void updateRequestDetails(String cacheKey, String status, CacheDTO cacheDTO) throws JsonProcessingException { + log.info("Updated cache status"); + + cacheDTO.setStatus(status); + cacheDTO.setLastUpdatedDate(CommonUtils.getCurrentTimeStamp()); + ValueOperations val = redisTemplate.opsForValue(); + val.set(cacheKey, new ObjectMapper().writeValueAsString(cacheDTO)); + } + + /** + * Get all cache keys from Redis + * + * @param cacheKeySearchString required unique to DP + * @return List of cache keys + */ + @Override + public List getCacheKeys(String cacheKeySearchString) { + Set redisKeys = redisTemplate.keys(cacheKeySearchString); + + return new ArrayList<>(Objects.requireNonNull(redisKeys)); + } + + /** + * Geta a single request with its cache key + * + * @param cacheKey required unique to DP + * @return Request data + */ + @Override + public String getRequestData(String cacheKey) { + return redisTemplate.opsForValue().get(cacheKey); + } +} diff --git a/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/utils/DpCommonUtils.java b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/utils/DpCommonUtils.java new file mode 100644 index 0000000..8debd68 --- /dev/null +++ b/g2pc-dp-core-lib/src/main/java/g2pc/dp/core/lib/utils/DpCommonUtils.java @@ -0,0 +1,105 @@ +package g2pc.dp.core.lib.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.security.BearerTokenUtil; +import g2pc.core.lib.security.service.G2pTokenService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class DpCommonUtils { + + @Value("${keycloak.dp.client.realm}") + private String keycloakRealm; + + @Value("${keycloak.dp.master.getClientUrl}") + private String getClientUrl; + + @Value("${crypto.to_dc.support_encryption}") + private boolean isEncrypt; + + @Value("${crypto.to_dc.support_signature}") + private boolean isSign; + + @Value("${keycloak.dp.client.url}") + private String keycloakURL; + + @Value("${keycloak.dp.client.clientId}") + private String keycloakClientId; + + @Value("${keycloak.dp.client.clientSecret}") + private String keycloakClientSecret; + + @Value("${keycloak.dp.master.url}") + private String masterUrl; + + @Value("${keycloak.dp.master.clientId}") + private String masterClientId; + + @Value("${keycloak.dp.master.clientSecret}") + private String masterClientSecret; + + @Value("${keycloak.dp.username}") + private String adminUsername; + + @Value("${keycloak.dp.password}") + private String adminPassword; + + @Autowired + G2pTokenService g2pTokenService; + + @Value("${sftp.dc.host}") + private String sftpDcHost; + + @Value("${sftp.dc.port}") + private int sftpDcPort; + + @Value("${sftp.dc.user}") + private String sftpDcUser; + + @Value("${sftp.dc.password}") + private String sftpDcPassword; + + @Value("${sftp.dc.remote.outbound_directory}") + private String sftpDcRemoteOutboundDirectory; + + + public void handleToken() throws G2pHttpException, JsonProcessingException { + log.info("Is encrypted ? -> " + isEncrypt); + log.info("Is signed ? -> " + isSign); + String token = BearerTokenUtil.getBearerTokenHeader(); + String introspectUrl = keycloakURL + "/introspect"; + ResponseEntity introspectResponse = g2pTokenService.getInterSpectResponse(introspectUrl, token, + keycloakClientId, keycloakClientSecret); + log.info("Introspect response -> " + introspectResponse.getStatusCode()); + log.info("Introspect response body -> " + introspectResponse.getBody()); + if (introspectResponse.getStatusCode().value() == 401) { + throw new G2pHttpException(new G2pcError(introspectResponse.getStatusCode().toString(), introspectResponse.getBody())); + } + if (!g2pTokenService.validateToken(masterUrl, getClientUrl, + g2pTokenService.decodeToken(token), masterClientId, masterClientSecret, + adminUsername, adminPassword)) { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_USER_UNAUTHORIZED.toValue(), "User is not authorized")); + } + } + + public SftpServerConfigDTO getSftpConfigForDp() { + SftpServerConfigDTO sftpServerConfigDTO = new SftpServerConfigDTO(); + sftpServerConfigDTO.setHost(sftpDcHost); + sftpServerConfigDTO.setPort(sftpDcPort); + sftpServerConfigDTO.setUser(sftpDcUser); + sftpServerConfigDTO.setPassword(sftpDcPassword); + sftpServerConfigDTO.setAllowUnknownKeys(true); + sftpServerConfigDTO.setStrictHostKeyChecking("no"); + sftpServerConfigDTO.setRemoteOutboundDirectory(sftpDcRemoteOutboundDirectory); + return sftpServerConfigDTO; + } +} diff --git a/g2pc-dp-core-lib/src/main/resources/application.yml b/g2pc-dp-core-lib/src/main/resources/application.yml new file mode 100644 index 0000000..aba780c --- /dev/null +++ b/g2pc-dp-core-lib/src/main/resources/application.yml @@ -0,0 +1,111 @@ + +spring: + mvc: + pathmatch: + matching-strategy: ANT_PATH_MATCHER + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + +spring.data.redis: + repositories.enabled: false + host: not_set + password: not_set + port: not_set + +crypto: + to_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + id: not_set + from_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + +client: + api_urls: + client_search_api: not_set + client_status_api: not_set + +keycloak: + from_dc: + url: not_set + clientId: not_set + clientSecret: not_set + dp: + url: not_set + username: not_set + password: not_set + master: + url: not_set + getClientUrl: not_set + clientId: not_set + clientSecret: not_set + client: + url: not_set + realm: not_set + clientId: not_set + clientSecret: not_set + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dc: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + outbound_directory: not_set + + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + enabled: not_set + elasticsearch: + host: not_set + port: not_set + scheme: not_set \ No newline at end of file diff --git a/g2pc-dp-core-lib/src/test/java/g2pc/dp/core/lib/G2pcDpCoreLibraryApplicationTests.java b/g2pc-dp-core-lib/src/test/java/g2pc/dp/core/lib/G2pcDpCoreLibraryApplicationTests.java new file mode 100644 index 0000000..4178ad7 --- /dev/null +++ b/g2pc-dp-core-lib/src/test/java/g2pc/dp/core/lib/G2pcDpCoreLibraryApplicationTests.java @@ -0,0 +1,13 @@ +package g2pc.dp.core.lib; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class G2pcDpCoreLibraryApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/g2pc-reference-apps/README.md b/g2pc-reference-apps/README.md new file mode 100644 index 0000000..4fbf326 --- /dev/null +++ b/g2pc-reference-apps/README.md @@ -0,0 +1,92 @@ +# g2pc-reference-apps + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://git.tekdi.net/cdpi-farmer-input-support-program/g2pc-reference-apps.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://git.tekdi.net/cdpi-farmer-input-support-program/g2pc-reference-apps/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/.gitignore b/g2pc-reference-apps/g2pc-ref-dc-client/.gitignore new file mode 100644 index 0000000..e23d88f --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +mvnw +mvnw.cmd +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +.idea/ + diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/Dockerfile b/g2pc-reference-apps/g2pc-ref-dc-client/Dockerfile new file mode 100644 index 0000000..5ff45e5 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/Dockerfile @@ -0,0 +1,3 @@ +FROM openjdk:17-jdk-alpine +COPY target/*.war dc-client-0.1.war +ENTRYPOINT ["java","-jar","/dc-client-0.1.war"] \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/README.md b/g2pc-reference-apps/g2pc-ref-dc-client/README.md new file mode 100644 index 0000000..2191629 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/README.md @@ -0,0 +1 @@ +# G2pc Ref DC Client \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/pom.xml b/g2pc-reference-apps/g2pc-ref-dc-client/pom.xml new file mode 100644 index 0000000..ce48ac1 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.0.12 + + + war + g2pc.ref.dc.client + g2pc-ref-dc-client + 0.0.1-SNAPSHOT + g2pc-ref-dc-client + g2pc-ref-dc-client + + 17 + + + + org.springframework.boot + spring-boot-starter + + + org.projectlombok + lombok + true + + + org.postgresql + postgresql + 42.5.4 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + g2pc.dc.core.lib + g2pc-dc-core-library + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.springdoc + springdoc-openapi-ui + 1.6.15 + + + com.networknt + json-schema-validator + 1.0.82 + + + jakarta.validation + jakarta.validation-api + 2.0.2 + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + javax.servlet + jstl + 1.2 + + + org.springframework.boot + spring-boot-devtools + true + + + javax.servlet.jsp.jstl + javax.servlet.jsp.jstl-api + 1.2.1 + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + 7.10.2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/G2pcRefDcClientApplication.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/G2pcRefDcClientApplication.java new file mode 100644 index 0000000..6fb5d2f --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/G2pcRefDcClientApplication.java @@ -0,0 +1,45 @@ +package g2pc.ref.dc.client; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.scheduling.annotation.EnableScheduling; + +@ComponentScan({"g2pc.ref.dc.client", "g2pc.core.lib", "g2pc.dc.core.lib", "g2pc.ref.dc.client"}) +@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) +@EnableScheduling +public class G2pcRefDcClientApplication { + + public static void main(String[] args) { + SpringApplication.run(G2pcRefDcClientApplication.class, args); + } + + @Value("${spring.second-datasource.url}") + private String url; + + @Value("${spring.second-datasource.username}") + private String username; + + @Value("${spring.second-datasource.password}") + private String password; + + @Value("${spring.second-datasource.driverClassName}") + private String driverClassName; + + JdbcTemplate jdbcTemplate; + + @PostConstruct + public void init() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(driverClassName); + dataSource.setUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); + jdbcTemplate = new JdbcTemplate(dataSource); + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/JdbcConfig.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/JdbcConfig.java new file mode 100644 index 0000000..92c5f6f --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/JdbcConfig.java @@ -0,0 +1,35 @@ +package g2pc.ref.dc.client.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.stereotype.Service; + +import javax.sql.DataSource; + +@Service +public class JdbcConfig{ + + @Value("${spring.second-datasource.url}") + private String url; + + @Value("${spring.second-datasource.username}") + private String username; + + @Value("${spring.second-datasource.password}") + private String password; + + @Value("${spring.second-datasource.driverClassName}") + private String driverClassName; + + public JdbcTemplate getJdbcTemplate() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(driverClassName); + dataSource.setUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); + return new JdbcTemplate(dataSource); + } +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/ObjectMapperConfig.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/ObjectMapperConfig.java new file mode 100644 index 0000000..354c144 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/ObjectMapperConfig.java @@ -0,0 +1,19 @@ +package g2pc.ref.dc.client.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ObjectMapperConfig { + + @Bean + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class); + return objectMapper; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/RegistryConfig.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/RegistryConfig.java new file mode 100644 index 0000000..4ffc971 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/config/RegistryConfig.java @@ -0,0 +1,237 @@ +package g2pc.ref.dc.client.config; + +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.constants.SftpConstants; +import g2pc.core.lib.enums.SortOrderEnum; +import g2pc.ref.dc.client.constants.Constants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import java.util.HashMap; +import java.util.Map; + +@Service +@Slf4j +public class RegistryConfig { + + @Value("${registry.api_urls.farmer_search_api}") + private String farmerSearchURL; + + @Value("${registry.api_urls.mobile_search_api}") + private String mobileSearchURL; + + @Value("${dashboard.clear_dp1_db_endpoint_url}") + private String farmerClearDbURL; + + @Value("${dashboard.clear_dp2_db_endpoint_url}") + private String mobileClearDbURL; + + @Value("${keycloak.from_dp.farmer.clientId}") + private String farmerClientId; + + @Value("${keycloak.from_dp.farmer.clientSecret}") + private String farmerClientSecret; + + @Value("${keycloak.from_dp.farmer.url}") + private String keycloakFarmerTokenUrl; + + @Value("${keycloak.from_dp.mobile.url}") + private String keycloakMobileTokenUrl; + + @Value("${keycloak.from_dp.mobile.clientId}") + private String mobileClientId; + + @Value("${keycloak.from_dp.mobile.clientSecret}") + private String mobileClientSecret; + + @Value("${crypto.to_dp_farmer.support_encryption}") + private boolean isFarmerEncrypt; + + @Value("${crypto.to_dp_farmer.support_signature}") + private boolean isFarmerSign; + + @Value("${crypto.to_dp_mobile.support_encryption}") + private boolean isMobileEncrypt; + + @Value("${crypto.to_dp_mobile.support_signature}") + private boolean isMobileSign; + + @Value("${crypto.to_dp_mobile.key_path}") + private String mobileKeyPath; + + @Value("${crypto.to_dp_farmer.key_path}") + private String farmerKeyPath; + + @Value("${crypto.to_dp_farmer.password}") + private String farmerKeyPassword; + + @Value("${crypto.to_dp_mobile.password}") + private String mobileKeyPassword; + + @Value("${sftp.dp1.host}") + private String sftpDp1Host; + + @Value("${sftp.dp1.port}") + private int sftpDp1Port; + + @Value("${sftp.dp1.user}") + private String sftpDp1User; + + @Value("${sftp.dp1.password}") + private String sftpDp1Password; + + @Value("${sftp.dp1.remote.inbound_directory}") + private String sftpDp1RemoteInboundDirectory; + + @Value("${sftp.dp2.host}") + private String sftpDp2Host; + + @Value("${sftp.dp2.port}") + private int sftpDp2Port; + + @Value("${sftp.dp2.user}") + private String sftpDp2User; + + @Value("${sftp.dp2.password}") + private String sftpDp2Password; + + @Value("${sftp.dp2.remote.inbound_directory}") + private String sftpDp2RemoteInboundDirectory; + + @Value("${crypto.sample.password}") + private String dummyKeyPassword; + + @Value("${crypto.sample.key.path}") + private String dummyKeyPath; + + @Value("${registry.api_urls.farmer_status_api}") + private String farmerStatusUrl; + + @Value("${registry.api_urls.mobile_status_api}") + private String mobileStatusUrl; + + + + /** + * Map to represent which query params are required for which registry + * + * @return query params specific to registry + */ + public Map getQueryParamsConfig() { + Map queryParamsConfig = new HashMap<>(); + + Map farmerRegistryMap = new HashMap<>(); + farmerRegistryMap.put("farmer_id", ""); + farmerRegistryMap.put("season", ""); + farmerRegistryMap.put("status", ""); + + + Map mobileRegistryMap = new HashMap<>(); + mobileRegistryMap.put("mobile_number", ""); + mobileRegistryMap.put("season", ""); + mobileRegistryMap.put("status", ""); + + queryParamsConfig.put(Constants.FARMER_REGISTRY, farmerRegistryMap); + queryParamsConfig.put(Constants.MOBILE_REGISTRY, mobileRegistryMap); + return queryParamsConfig; + } + + /** + * Map to represent which common values to be used to generate request for a registry + * + * @return Map to represent registry specific config values + */ + public Map getRegistrySpecificConfig(String isSignEncrypt) { + Map queryParamsConfig = new HashMap<>(); + + Map farmerRegistryMap = getFarmerRegistryMap(isSignEncrypt); + Map mobileRegistryMap = getMobileRegistryMap(isSignEncrypt); + + queryParamsConfig.put(Constants.FARMER_REGISTRY, farmerRegistryMap); + queryParamsConfig.put(Constants.MOBILE_REGISTRY, mobileRegistryMap); + return queryParamsConfig; + } + + /** + * Set farmer registry specific config values + * + * @return Map to represent registry specific config values for farmer + */ + private Map getFarmerRegistryMap(String isSignEncrypt) { + Map farmerRegistryMap = new HashMap<>(); + farmerRegistryMap.put(CoreConstants.QUERY_NAME, "paid_farmer"); + farmerRegistryMap.put(CoreConstants.REG_TYPE, "ns:FARMER_REGISTRY"); + farmerRegistryMap.put(CoreConstants.REG_SUB_TYPE, ""); + farmerRegistryMap.put(CoreConstants.QUERY_TYPE, "namedQuery"); + farmerRegistryMap.put(CoreConstants.SORT_ATTRIBUTE, "farmer_id"); + farmerRegistryMap.put(CoreConstants.SORT_ORDER, SortOrderEnum.ASC.toValue()); + farmerRegistryMap.put(CoreConstants.PAGE_NUMBER, "1"); + farmerRegistryMap.put(CoreConstants.PAGE_SIZE, "10"); + farmerRegistryMap.put(CoreConstants.KEYCLOAK_URL, keycloakFarmerTokenUrl); + farmerRegistryMap.put(CoreConstants.KEYCLOAK_CLIENT_ID, farmerClientId); + farmerRegistryMap.put(CoreConstants.KEYCLOAK_CLIENT_SECRET, farmerClientSecret); + farmerRegistryMap.put(CoreConstants.SUPPORT_ENCRYPTION, "" + isFarmerEncrypt); + farmerRegistryMap.put(CoreConstants.SUPPORT_SIGNATURE, "" + isFarmerSign); + if(isSignEncrypt.equals("2")){ + farmerRegistryMap.put(CoreConstants.KEY_PATH, dummyKeyPath); + farmerRegistryMap.put(CoreConstants.KEY_PASSWORD, dummyKeyPassword); + } else { + farmerRegistryMap.put(CoreConstants.KEY_PATH, farmerKeyPath); + farmerRegistryMap.put(CoreConstants.KEY_PASSWORD, farmerKeyPassword); + } + + farmerRegistryMap.put(CoreConstants.DP_SEARCH_URL, farmerSearchURL); + farmerRegistryMap.put(CoreConstants.DP_CLEAR_DB_URL, farmerClearDbURL); + farmerRegistryMap.put(SftpConstants.SFTP_HOST, sftpDp1Host); + farmerRegistryMap.put(SftpConstants.SFTP_PORT, String.valueOf(sftpDp1Port)); + farmerRegistryMap.put(SftpConstants.SFTP_USER, sftpDp1User); + farmerRegistryMap.put(SftpConstants.SFTP_PASSWORD, sftpDp1Password); + farmerRegistryMap.put(SftpConstants.SFTP_SESSION_CONFIG, "no"); + farmerRegistryMap.put(SftpConstants.SFTP_ALLOW_UNKNOWN_KEYS, String.valueOf(true)); + farmerRegistryMap.put(SftpConstants.SFTP_REMOTE_INBOUND_DIRECTORY, sftpDp1RemoteInboundDirectory); + farmerRegistryMap.put(CoreConstants.DP_STATUS_URL , farmerStatusUrl); + + return farmerRegistryMap; + } + + /** + * Set mobile registry specific config values + * + * @return Map to represent registry specific config values for mobile + */ + private Map getMobileRegistryMap(String isSignEncrypt) { + Map mobileRegistryMap = new HashMap<>(); + mobileRegistryMap.put(CoreConstants.QUERY_NAME, "mobile_registered"); + mobileRegistryMap.put(CoreConstants.REG_TYPE, "ns:MOBILE_REGISTRY"); + mobileRegistryMap.put(CoreConstants.REG_SUB_TYPE, ""); + mobileRegistryMap.put(CoreConstants.QUERY_TYPE, "namedQuery"); + mobileRegistryMap.put(CoreConstants.SORT_ATTRIBUTE, "mobile_number"); + mobileRegistryMap.put(CoreConstants.SORT_ORDER, SortOrderEnum.ASC.toValue()); + mobileRegistryMap.put(CoreConstants.PAGE_NUMBER, "1"); + mobileRegistryMap.put(CoreConstants.PAGE_SIZE, "10"); + mobileRegistryMap.put(CoreConstants.KEYCLOAK_URL, keycloakMobileTokenUrl); + mobileRegistryMap.put(CoreConstants.KEYCLOAK_CLIENT_ID, mobileClientId); + mobileRegistryMap.put(CoreConstants.KEYCLOAK_CLIENT_SECRET, mobileClientSecret); + mobileRegistryMap.put(CoreConstants.SUPPORT_ENCRYPTION, "" + isMobileEncrypt); + mobileRegistryMap.put(CoreConstants.SUPPORT_SIGNATURE, "" + isMobileSign); + if(isSignEncrypt.equals("2")){ + mobileRegistryMap.put(CoreConstants.KEY_PATH, dummyKeyPath); + mobileRegistryMap.put(CoreConstants.KEY_PASSWORD, dummyKeyPassword); + } + else{ + mobileRegistryMap.put(CoreConstants.KEY_PATH, mobileKeyPath); + mobileRegistryMap.put(CoreConstants.KEY_PASSWORD, mobileKeyPassword); + } + mobileRegistryMap.put(CoreConstants.DP_SEARCH_URL, mobileSearchURL); + mobileRegistryMap.put(CoreConstants.DP_CLEAR_DB_URL, mobileClearDbURL); + mobileRegistryMap.put(SftpConstants.SFTP_HOST, sftpDp2Host); + mobileRegistryMap.put(SftpConstants.SFTP_PORT, String.valueOf(sftpDp2Port)); + mobileRegistryMap.put(SftpConstants.SFTP_USER, sftpDp2User); + mobileRegistryMap.put(SftpConstants.SFTP_PASSWORD, sftpDp2Password); + mobileRegistryMap.put(SftpConstants.SFTP_SESSION_CONFIG, "no"); + mobileRegistryMap.put(SftpConstants.SFTP_ALLOW_UNKNOWN_KEYS, String.valueOf(true)); + mobileRegistryMap.put(SftpConstants.SFTP_REMOTE_INBOUND_DIRECTORY, sftpDp2RemoteInboundDirectory); + mobileRegistryMap.put(CoreConstants.DP_STATUS_URL,mobileStatusUrl); + return mobileRegistryMap; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/constants/Constants.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/constants/Constants.java new file mode 100644 index 0000000..2ca7a28 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/constants/Constants.java @@ -0,0 +1,33 @@ +package g2pc.ref.dc.client.constants; + +public class Constants { + + private Constants() { + } + + public static final String SEARCH_REQUEST_RECEIVED = "Search request received successfully"; + + public static final String INVALID_RESPONSE = "Invalid Response received from server"; + + public static final String CONFLICT = "CONFLICT"; + + public static final String INVALID_AUTHORIZATION = "Invalid Authorization"; + + public static final String PENDING = "PENDING"; + + public static final String COMPLETED = "COMPLETED"; + + public static final String ON_SEARCH_RESPONSE_RECEIVED = "On-Search response received successfully"; + + public static final String ON_STATUS_RESPONSE_RECEIVED = "On-Status response received successfully"; + + public static final String FARMER_REGISTRY = "farmer_registry"; + + public static final String MOBILE_REGISTRY = "mobile_registry"; + + public static final String CONFIGURATION_MISMATCH_ERROR = "Configurations are not matching "; + + public static final String UPLOAD_ERROR = "Failed to upload file:"; + + public static final String GENERATE_REQUEST_ERROR_MESSAGE = "Exception in generateRequest : "; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/rest/DcController.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/rest/DcController.java new file mode 100644 index 0000000..6b0165d --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/rest/DcController.java @@ -0,0 +1,439 @@ +package g2pc.ref.dc.client.controller.rest; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.config.G2pUnirestHelper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseMessageDTO; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.exceptionhandler.ErrorResponse; +import g2pc.core.lib.exceptionhandler.ValidationErrorResponse; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import g2pc.core.lib.service.ElasticsearchService; +import g2pc.core.lib.service.SftpHandlerService; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dc.core.lib.entity.ResponseTrackerEntity; +import g2pc.dc.core.lib.repository.ResponseDataRepository; +import g2pc.dc.core.lib.repository.ResponseTrackerRepository; +import g2pc.dc.core.lib.service.RequestBuilderService; +import g2pc.ref.dc.client.config.JdbcConfig; +import g2pc.ref.dc.client.config.RegistryConfig; +import g2pc.ref.dc.client.constants.Constants; +import g2pc.ref.dc.client.dto.dashboard.HttpsLeftPanelDataDTO; +import g2pc.ref.dc.client.service.DcRequestBuilderService; +import g2pc.ref.dc.client.service.DcResponseHandlerService; +import g2pc.ref.dc.client.service.DcSftpPushUpdateService; +import g2pc.ref.dc.client.service.DcValidationService; +import g2pc.ref.dc.client.utils.DcCommonUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import kong.unirest.HttpResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import javax.annotation.PostConstruct; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +@RestController +@Slf4j +@RequestMapping(produces = "application/json") +@Tag(name = "Data Consumer", description = "DC APIs") +public class DcController { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Autowired + private DcRequestBuilderService dcRequestBuilderService; + + @Autowired + private DcResponseHandlerService dcResponseHandlerService; + + @Autowired + DcValidationService dcValidationService; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Autowired + private DcCommonUtils commonUtils; + + @Autowired + private ResponseDataRepository responseDataRepository; + + @Autowired + private RequestBuilderService requestBuilderService; + + @Autowired + private RegistryConfig registryConfig; + + @Autowired + private G2pUnirestHelper g2pUnirestHelper; + + @Autowired + private SftpHandlerService sftpHandlerService; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private ResponseTrackerRepository responseTrackerRepository; + + @Autowired + private DcSftpPushUpdateService dcSftpPushUpdateService; + + @Autowired + private ElasticsearchService elasticsearchService; + + @Value("${spring.second-datasource.url}") + private String url; + + @Value("${spring.second-datasource.username}") + private String username; + + @Value("${spring.second-datasource.password}") + private String password; + + @Value("${spring.second-datasource.driverClassName}") + private String driverClassName; + + @Autowired + private JdbcConfig jdbcConfig; + + private JdbcTemplate jdbcTemplate; + + @PostConstruct + public void init() { + jdbcTemplate=jdbcConfig.getJdbcTemplate(); + } + + /** + * Get consumer search request + * + * @param payloadMap required + * @return Search request received acknowledgement + */ + @Operation(summary = "Receive consumer search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/public/api/v1/consumer/search/payload") + public AcknowledgementDTO createSearchRequestsFromPayload(@RequestBody Map payloadMap) throws Exception { + log.info("Payload received from postman"); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + if (ObjectUtils.isNotEmpty(payloadMap)) { + acknowledgementDTO = dcRequestBuilderService.generateRequest(Collections.singletonList(payloadMap), + CoreConstants.SEND_PROTOCOL_HTTPS, "", "", ""); + } + return acknowledgementDTO; + } + + /** + * Get consumer search request + * + * @param payloadFile required + * @return Search request received acknowledgement + */ + @Operation(summary = "Receive consumer search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/public/api/v1/consumer/search/csv") + public AcknowledgementDTO createSearchRequestsFromCsv(@RequestPart(value = "file") MultipartFile payloadFile) throws Exception { + log.info("Payload received from csv file"); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + if (ObjectUtils.isNotEmpty(payloadFile)) { + Path tempFile = Paths.get(System.getProperty("java.io.tmpdir"), "payload.csv"); + if (Files.exists(tempFile)) { + commonUtils.deleteFolder(tempFile); + } + Files.createFile(tempFile); + payloadFile.transferTo(tempFile.toFile()); + acknowledgementDTO = dcRequestBuilderService.generateRequest( + requestBuilderService.generatePayloadFromCsv(tempFile.toFile()), CoreConstants.SEND_PROTOCOL_HTTPS, + dcRequestBuilderService.demoTestEncryptionSignature(tempFile.toFile()), + payloadFile.getName(), ""); + Files.delete(tempFile); + } + return acknowledgementDTO; + } + + /** + * Listen to registry response + * + * @param responseString required + * @return On-Search response received acknowledgement + */ + @SuppressWarnings("unchecked") + @Operation(summary = "Listen to registry response") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.ON_SEARCH_RESPONSE_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/registry/on-search") + public AcknowledgementDTO handleOnSearchResponse(@RequestBody String responseString) throws Exception { + commonUtils.handleToken(); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + ResponseDTO responseDTO = objectMapper.readerFor(ResponseDTO.class). + readValue(responseString); + ResponseMessageDTO messageDTO; + Map metaData = (Map) responseDTO.getHeader().getMeta().getData(); + messageDTO = dcValidationService.signatureValidation(metaData, responseDTO); + responseDTO.setMessage(messageDTO); + try { + dcValidationService.validateResponseDto(responseDTO); + if (ObjectUtils.isNotEmpty(responseDTO)) { + acknowledgementDTO = dcResponseHandlerService.getResponse(responseDTO, null, sunbirdEnabled); + } + } catch (JsonProcessingException | IllegalArgumentException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } + return acknowledgementDTO; + } + + + /** + * Handle validation exception error response. + * + * @param ex the ValidationException + * @return the error response + */ + @ExceptionHandler(value + = G2pcValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ValidationErrorResponse + handleValidationException( + G2pcValidationException ex) { + return new ValidationErrorResponse( + ex.getG2PcErrorList()); + } + + /** + * Handle validation exception error response. + * + * @param ex the ValidationException + * @return the error response + */ + @ExceptionHandler(value = G2pHttpException.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public ErrorResponse handleG2pHttpStatusException(G2pHttpException ex) { + return new ErrorResponse(ex.getG2PcError()); + + } + + /** + * Clear transaction tracker DB + */ + @SuppressWarnings("unchecked") + @GetMapping("/private/api/v1/registry/clear-db") + public void clearDb() throws G2pHttpException, IOException { + commonUtils.handleToken(); + responseDataRepository.deleteAll(); + log.info("DC DB cleared"); + + for (Map.Entry configEntryMap : registryConfig.getRegistrySpecificConfig("").entrySet()) { + try { + Map registrySpecificConfigMap = (Map) registryConfig.getRegistrySpecificConfig("").get(configEntryMap.getKey()); + String jwtToken = requestBuilderService.getValidatedToken(registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_URL).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_ID).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_SECRET).toString()); + log.info("jwtToken: {}", jwtToken); + log.info("url: {}", registrySpecificConfigMap.get(CoreConstants.DP_CLEAR_DB_URL).toString()); + HttpResponse response = g2pUnirestHelper.g2pGet(registrySpecificConfigMap.get(CoreConstants.DP_CLEAR_DB_URL).toString()) + .header("Content-Type", "application/json") + .header("Authorization", jwtToken) + .asString(); + log.info("DP " + registrySpecificConfigMap.get(CoreConstants.REG_TYPE) + " DB cleared with response " + response.getStatus()); + } catch (Exception e) { + log.error("Exception in clearDb: ", e); + } + } + try { + jdbcTemplate.execute("DELETE FROM \"V_Response_Tracker\""); + jdbcTemplate.execute("DELETE FROM \"V_Response_Data\""); + jdbcTemplate.execute("DELETE FROM \"V_Msg_Tracker\""); + jdbcTemplate.execute("DELETE FROM \"V_Txn_Tracker\""); + log.info("Sunbird DB data cleared"); + } catch (Exception e) { + log.error("Exception in clear Sunbird DB : ", e); + } + try { + elasticsearchService.clearData("msg_tracker"); + elasticsearchService.clearData("txn_tracker"); + elasticsearchService.clearData("response_tracker"); + elasticsearchService.clearData("response_data"); + log.info("Sunbird elastic data cleared"); + } catch (Exception e) { + log.error("Sunbird elastic data cleared: ", e); + } + Set keys = redisTemplate.keys("*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + log.info("DC Redis cache cleared"); + } + + @Operation(summary = "Receive consumer search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/consumer/status/payload") + public AcknowledgementDTO createStatusRequest(@RequestParam String transactionId, @RequestParam String transactionType) throws Exception { + log.info("Payload received for status"); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + if (ObjectUtils.isNotEmpty(transactionId)) { + acknowledgementDTO = dcRequestBuilderService.generateStatusRequest(transactionId, transactionType, CoreConstants.SEND_PROTOCOL_HTTPS); + } + return acknowledgementDTO; + } + + /** + * Listen to registry response + * + * @param responseString required + * @return On-status response received acknowledgement + */ + @SuppressWarnings("unchecked") + @Operation(summary = "Listen to registry response") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.ON_SEARCH_RESPONSE_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/registry/on-status") + public AcknowledgementDTO handleOnStatusResponse(@RequestBody String responseString) throws Exception { + commonUtils.handleToken(); + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + StatusResponseDTO statusResponseDTO = objectMapper.readerFor(StatusResponseDTO.class). + readValue(responseString); + StatusResponseMessageDTO statusResponseMessageDTO; + Map metaData = (Map) statusResponseDTO.getHeader().getMeta().getData(); + statusResponseMessageDTO = dcValidationService.signatureValidation(metaData, statusResponseDTO); + statusResponseDTO.setMessage(statusResponseMessageDTO); + try { + dcValidationService.validateStatusResponseDTO(statusResponseDTO); + if (ObjectUtils.isNotEmpty(statusResponseDTO)) { + acknowledgementDTO = dcResponseHandlerService.getStatusResponse(statusResponseDTO); + } + } catch (JsonProcessingException | IllegalArgumentException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } + return acknowledgementDTO; + } + + /** + * Listen to CSV file payload to handle using SFTP + * + * @param file required + * @return AcknowledgementDTO + */ + @Operation(summary = "Listen to CSV file payload to handle using SFTP") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping(value = "/public/api/v1/consumer/search/sftp/csv") + public AcknowledgementDTO createStatusRequestSftp(@RequestParam("file") MultipartFile file) { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + try { + if (!Objects.equals(file.getContentType(), "text/csv")) { + acknowledgementDTO.setStatus(HeaderStatusENUM.RJCT.toValue()); + acknowledgementDTO.setMessage("Invalid file type"); + return acknowledgementDTO; + } + String originalFilename = UUID.randomUUID() + ".csv"; + Path tempFile = Paths.get(System.getProperty("java.io.tmpdir"), originalFilename); + Files.createFile(tempFile); + file.transferTo(tempFile.toFile()); + SftpServerConfigDTO sftpServerConfigDTO = commonUtils.getSftpConfigForDc(); + Boolean status = sftpHandlerService.uploadFileToSftp(sftpServerConfigDTO, tempFile.toString(), + sftpServerConfigDTO.getRemoteInboundDirectory()); + Files.delete(tempFile); + if (Boolean.FALSE.equals(status)) { + acknowledgementDTO.setStatus(HeaderStatusENUM.RJCT.toValue()); + acknowledgementDTO.setMessage(Constants.UPLOAD_ERROR); + return acknowledgementDTO; + } + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + acknowledgementDTO.setMessage("File uploaded successfully"); + } catch (IOException e) { + log.error(Constants.UPLOAD_ERROR, e); + acknowledgementDTO.setStatus(HeaderStatusENUM.RJCT.toValue()); + acknowledgementDTO.setMessage(Constants.UPLOAD_ERROR); + } + return acknowledgementDTO; + } + + @GetMapping("/dashboard/leftPanel/data") + public List fetchLeftPanelData() { + List leftPanelDataDTOList; + if (Boolean.FALSE.equals(sunbirdEnabled)) { + leftPanelDataDTOList = new ArrayList<>(); + Optional> optionalList = responseTrackerRepository.findAllByAction("search"); + if (optionalList.isEmpty()) { + return leftPanelDataDTOList; + } + List responseTrackerEntityList = optionalList.get(); + for (ResponseTrackerEntity responseTrackerEntity : responseTrackerEntityList) { + HttpsLeftPanelDataDTO leftPanelDataDTO = new HttpsLeftPanelDataDTO(); + leftPanelDataDTO.setMessageTs(responseTrackerEntity.getMessageTs()); + leftPanelDataDTO.setTransactionId(responseTrackerEntity.getTransactionId()); + leftPanelDataDTO.setStatus(responseTrackerEntity.getStatus()); + leftPanelDataDTOList.add(leftPanelDataDTO); + } + } else { + String sql = "SELECT message_ts, transaction_id, status FROM \"V_Response_Tracker\" WHERE action = 'search'"; + leftPanelDataDTOList = jdbcTemplate.query(sql, (rs, rowNum) -> { + HttpsLeftPanelDataDTO leftPanelDataDTO = new HttpsLeftPanelDataDTO(); + leftPanelDataDTO.setMessageTs(rs.getString("message_ts")); + leftPanelDataDTO.setTransactionId(rs.getString("transaction_id")); + leftPanelDataDTO.setStatus(rs.getString("status")); + return leftPanelDataDTO; + }); + } + return leftPanelDataDTOList; + } + + @GetMapping(value = "/dashboard/sftp/dc/data", produces = "text/event-stream") + public SseEmitter sseEmitterFirstPanel() { + return dcSftpPushUpdateService.register(); + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/rest/DcDashboardController.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/rest/DcDashboardController.java new file mode 100644 index 0000000..ed43367 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/rest/DcDashboardController.java @@ -0,0 +1,145 @@ +package g2pc.ref.dc.client.controller.rest; + +import g2pc.dc.core.lib.service.RequestBuilderService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +import java.io.IOException; +import java.text.ParseException; + +@Controller +@Slf4j +public class DcDashboardController { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Value("${dashboard.left_panel_url}") + private String leftPanelUrl; + + @Value("${dashboard.right_panel_url}") + private String rightPanelUrl; + + @Value("${dashboard.bottom_panel_url}") + private String bottomPanelUrl; + + @Value("${dashboard.post_https_endpoint_url}") + private String postHttpsEndpointUrl; + + @Value("${dashboard.clear_dc_db_endpoint_url}") + private String clearDcDbEndpointUrl; + + @Value("${keycloak.dc.client.url}") + private String dcKeyCloakUrl; + + @Value("${keycloak.dc.client.clientId}") + private String dcClientId; + + @Value("${keycloak.dc.client.clientSecret}") + private String dcClientSecret; + + @Value("${dashboard.left_panel_data_endpoint_url}") + private String leftPanelDataEndpointUrl; + + @Value("${dashboard.sftp_post_endpoint_url}") + private String sftpPostEndpointUrl; + + @Value("${dashboard.sftp_dc_data_endpoint_url}") + private String sftpDcDataEndpointUrl; + + @Value("${dashboard.sftp_dp1_data_endpoint_url}") + private String sftpDp1DataEndpointUrl; + + @Value("${dashboard.sftp_dp2_data_endpoint_url}") + private String sftpDp2DataEndpointUrl; + + @Value("${dashboard.dc_status_endpoint_url}") + private String dcStatusEndpointUrl; + + @Value("${dashboard.sftp_left_panel_url}") + private String sftpLeftPanelUrl; + + @Value("${dashboard.sftp_right_panel_url}") + private String sftpRightPanelUrl; + + @Value("${dashboard.sftp_bottom_panel_url}") + private String sftpBottomPanelUrl; + + @Value("${dashboard.https_sunbird_left_panel_url}") + private String httpsSunbirdLeftPanelUrl; + + @Value("${dashboard.https_sunbird_right_panel_url}") + private String httpsSunbirdRightPanelUrl; + + @Value("${dashboard.https_sunbird_bottom_panel_url}") + private String httpsSunbirdBottomPanelUrl; + + @Value("${dashboard.sftp_sunbird_left_panel_url}") + private String sftpSunbirdLeftPanelUrl; + + @Value("${dashboard.sftp_sunbird_right_panel_url}") + private String sftpSunbirdRightPanelUrl; + + @Value("${dashboard.sftp_sunbird_bottom_panel_url}") + private String sftpSunbirdBottomPanelUrl; + + @Autowired + private RequestBuilderService requestBuilderService; + + @GetMapping("/dashboard/https") + public String showDashboardPage(Model model) throws IOException, ParseException { + String jwtToken = requestBuilderService.getValidatedToken(dcKeyCloakUrl, dcClientId, dcClientSecret); + if (Boolean.TRUE.equals(sunbirdEnabled)) { + model.addAttribute("left_panel_url", httpsSunbirdLeftPanelUrl); + model.addAttribute("right_panel_url", httpsSunbirdRightPanelUrl); + model.addAttribute("bottom_panel_url", httpsSunbirdBottomPanelUrl); + log.info("httpsSunbirdLeftPanelUrl: {}", httpsSunbirdLeftPanelUrl); + log.info("httpsSunbirdRightPanelUrl: {}", httpsSunbirdRightPanelUrl); + log.info("httpsSunbirdBottomPanelUrl: {}", httpsSunbirdBottomPanelUrl); + } else { + model.addAttribute("left_panel_url", leftPanelUrl); + model.addAttribute("right_panel_url", rightPanelUrl); + model.addAttribute("bottom_panel_url", bottomPanelUrl); + } + model.addAttribute("post_https_endpoint_url", postHttpsEndpointUrl); + model.addAttribute("clear_dc_db_endpoint_url", clearDcDbEndpointUrl); + model.addAttribute("jwtToken", jwtToken); + model.addAttribute("left_panel_data_endpoint_url", leftPanelDataEndpointUrl); + model.addAttribute("dc_status_endpoint_url", dcStatusEndpointUrl); + return "dashboardHttps"; + } + + @GetMapping("/dashboard/sftp") + public String showDashboardSftpPage(Model model) throws IOException, ParseException { + String jwtToken = requestBuilderService.getValidatedToken(dcKeyCloakUrl, dcClientId, dcClientSecret); + if (Boolean.TRUE.equals(sunbirdEnabled)) { + model.addAttribute("sftp_left_panel_url", sftpSunbirdLeftPanelUrl); + model.addAttribute("sftp_right_panel_url", sftpSunbirdRightPanelUrl); + model.addAttribute("sftp_bottom_panel_url", sftpSunbirdBottomPanelUrl); + } else { + model.addAttribute("sftp_left_panel_url", sftpLeftPanelUrl); + model.addAttribute("sftp_right_panel_url", sftpRightPanelUrl); + model.addAttribute("sftp_bottom_panel_url", sftpBottomPanelUrl); + } + model.addAttribute("sftp_post_endpoint_url", sftpPostEndpointUrl); + model.addAttribute("clear_dc_db_endpoint_url", clearDcDbEndpointUrl); + model.addAttribute("jwtToken", jwtToken); + return "dashboardSftp"; + } + + @GetMapping("/dashboard/sftp/sse") + public String showDashboardSftpPageSse(Model model) throws IOException, ParseException { + String jwtToken = requestBuilderService.getValidatedToken(dcKeyCloakUrl, dcClientId, dcClientSecret); + model.addAttribute("sftp_post_endpoint_url", sftpPostEndpointUrl); + model.addAttribute("sftp_dc_data_endpoint_url", sftpDcDataEndpointUrl); + model.addAttribute("sftp_dp1_data_endpoint_url", sftpDp1DataEndpointUrl); + model.addAttribute("sftp_dp2_data_endpoint_url", sftpDp2DataEndpointUrl); + model.addAttribute("clear_dc_db_endpoint_url", clearDcDbEndpointUrl); + model.addAttribute("jwtToken", jwtToken); + return "dashboardSftpSse"; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/sftp/DcSftpListener.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/sftp/DcSftpListener.java new file mode 100644 index 0000000..b684f6c --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/controller/sftp/DcSftpListener.java @@ -0,0 +1,139 @@ +package g2pc.ref.dc.client.controller.sftp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dc.core.lib.service.RequestBuilderService; +import g2pc.ref.dc.client.dto.dashboard.SftpDcData; +import g2pc.ref.dc.client.service.DcRequestBuilderService; +import g2pc.ref.dc.client.service.DcResponseHandlerService; +import g2pc.ref.dc.client.service.DcSftpPushUpdateService; +import g2pc.ref.dc.client.service.DcValidationService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.web.server.ResponseStatusException; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; + +@Configuration +@Slf4j +public class DcSftpListener { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpLocalDirectoryInbound; + + @Value("${sftp.listener.local.outbound_directory}") + private String sftpLocalDirectoryOutbound; + + @Autowired + private DcRequestBuilderService dcRequestBuilderService; + + @Autowired + private RequestBuilderService requestBuilderService; + + @Autowired + private DcResponseHandlerService dcResponseHandlerService; + + @Autowired + DcValidationService dcValidationService; + + @Autowired + private DcSftpPushUpdateService dcSftpPushUpdateService; + + private Queue dataQueue = new LinkedList<>(); + + /** + * Method used to handle input listener + * + * @param message + */ + @ServiceActivator(inputChannel = "sftpInbound") + public void handleMessageInbound(Message message) { + try { + File file = message.getPayload(); + log.info("Received Message from inbound directory: {}", file.getName()); + if (ObjectUtils.isNotEmpty(file) && file.getName().contains(".csv")) { + AcknowledgementDTO acknowledgementDTO = dcRequestBuilderService.generateRequest( + requestBuilderService.generatePayloadFromCsv(file), CoreConstants.SEND_PROTOCOL_SFTP, + dcRequestBuilderService.demoTestEncryptionSignature(file), file.getName(), null); + log.info("AcknowledgementDTO: {}", acknowledgementDTO); + } + Files.deleteIfExists(Path.of(sftpLocalDirectoryInbound + "/" + file.getName())); + } catch (Exception e) { + log.error("Error: ", e); + } + } + + /** + * Method used to handle output listener + * + * @param message message + * @throws Exception + */ + @SuppressWarnings("unchecked") + @ServiceActivator(inputChannel = "sftpOutbound") + public void handleMessageOutbound(Message message) { + try { + File file = message.getPayload(); + log.info("Received Message from outbound directory: {}", file.getName()); + if (ObjectUtils.isNotEmpty(file) && file.getName().contains(".json")) { + String responseString = new String(Files.readAllBytes(file.toPath())); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + ResponseDTO responseDTO = objectMapper.readerFor(ResponseDTO.class). + readValue(responseString); + ResponseMessageDTO messageDTO; + Map metaData = (Map) responseDTO.getHeader().getMeta().getData(); + messageDTO = dcValidationService.signatureValidation(metaData, responseDTO); + responseDTO.setMessage(messageDTO); + try { + dcValidationService.validateResponseDto(responseDTO); + if (ObjectUtils.isNotEmpty(responseDTO)) { + dcResponseHandlerService.getResponse(responseDTO, file.getName(), sunbirdEnabled); + } + log.info("on search response handled successfully"); + } catch (JsonProcessingException | IllegalArgumentException e) { + log.info("on search response handled error : ", e); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } + } + Files.deleteIfExists(Path.of(sftpLocalDirectoryOutbound + "/" + file.getName())); + } catch (Exception e) { + log.error("Error: ", e); + } + } + + /** + * Method to handle error + * + * @param message message + */ + @ServiceActivator(inputChannel = "errorChannel") + public void handleError(Message message) { + Throwable error = (Throwable) message.getPayload(); + log.error("Handling ERROR: {}", error.getMessage()); + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/dashboard/HttpsLeftPanelDataDTO.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/dashboard/HttpsLeftPanelDataDTO.java new file mode 100644 index 0000000..61b8f63 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/dashboard/HttpsLeftPanelDataDTO.java @@ -0,0 +1,17 @@ +package g2pc.ref.dc.client.dto.dashboard; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class HttpsLeftPanelDataDTO { + + private String messageTs; + + private String transactionId; + + private String status; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/dashboard/SftpDcData.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/dashboard/SftpDcData.java new file mode 100644 index 0000000..3afa907 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/dashboard/SftpDcData.java @@ -0,0 +1,19 @@ +package g2pc.ref.dc.client.dto.dashboard; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SftpDcData { + + private String messageTs; + + private String transactionId; + + private String fileName; + + private String sftpDirectoryType; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/farmer/response/RegRecordFarmerDTO.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/farmer/response/RegRecordFarmerDTO.java new file mode 100644 index 0000000..b0937ea --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/farmer/response/RegRecordFarmerDTO.java @@ -0,0 +1,30 @@ +package g2pc.ref.dc.client.dto.farmer.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class RegRecordFarmerDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("farmer_name") + private String farmerName; + + @JsonProperty("season") + private String season; + + @JsonProperty("payment_status") + private String paymentStatus; + + @JsonProperty("payment_date") + private String paymentDate; + + @JsonProperty("payment_amount") + private Double paymentAmount; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/mobile/response/DataMobileDTO.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/mobile/response/DataMobileDTO.java new file mode 100644 index 0000000..e2c84bb --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/mobile/response/DataMobileDTO.java @@ -0,0 +1,19 @@ +package g2pc.ref.dc.client.dto.mobile.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +public class DataMobileDTO{ + + @JsonProperty("reg_records") + private List regRecords; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/mobile/response/RegRecordMobileDTO.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/mobile/response/RegRecordMobileDTO.java new file mode 100644 index 0000000..cb20ede --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/dto/mobile/response/RegRecordMobileDTO.java @@ -0,0 +1,30 @@ +package g2pc.ref.dc.client.dto.mobile.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class RegRecordMobileDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("farmer_name") + private String farmerName; + + @JsonProperty("season") + private String season; + + @JsonProperty("mobile_number") + private String mobileNumber; + + @JsonProperty("mobile_status") + private String mobileStatus; + + @JsonProperty("created_date") + private String createdDate; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/farmer_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/farmer_search.p12 new file mode 100644 index 0000000..1873f5d Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/farmer_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private-key.pem new file mode 100644 index 0000000..c058bbb --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private-key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,4C9E4CBEA2B14DA0 + +lhzmy5pB4nVPuadAbAnJmkcj/L4Edk+/+W2eQuoyGkvlnRKpuEWb3IPT4CSZWGZN +pZdgzi2NDa3E/H/MzMpljeWLlKZvjjVExIfrJqQTD+xCzSWXKrU7xaujxp3VFjU9 +OR5n9qA9nzWUS077IUrhdDkmITXbtYMY41YXSWMzJX/QHiojRcEg9zkauoJ+Enbl +dD6FbQg8RbG254UnBmv+oblVcA9vvWQkRgMXxilXP2nMpibSbgNwU5LKPpBfiuH1 +NM3f/xv8dIQ5JfsaS27xzmUXlWO/fRJ6nO9liS+FguDZ2CU18npwVi+zBwLuhnsA +sJCX/9+7hNMXU2AZwMJdzM9h1eMksGqeFdPsue/sJF9HrZj37taOOh582ZGsZtx4 +c6JtRCf3Ni2SLt3Kip6b9Zv577XBlD8DvVQQiNcdeC6pzlJ7H8nMpCg/8BpO4y7+ +A4Quv+ecKNa1WzSoDMhbGmpcBlraY8WbpT3rPBkP9VURgmE5s6qsY0Dm95DiQgdp +V8cXF6+S0EGY0B9GvHcUW/B6RJ3WcXUmdJmqcElwul7gvicIgDur2222/V7H+gB2 +yWoZyktEgEzEZF5D1lgbZTg36WI+PPkJasg04iVHUmNvDGivy2+80gIu/YCVEMYc +rwnwFJMBIcgJM7Yd6purqb2QuSKueKqbnXxYUStubTIZ5839B3GcLPg0Y5xvsjH6 +4nnXKTNBAcLGv/ayZnscVZCYFXEgSwnyKPi9693vFADBQ9szAvZ2lHeNMKp+1zm2 +k2tjn7wwy7ii35x2Tjs2eT7C3r946/Le/dtRPNQ/gamsF77w7OBY2IaNLkuHDwZ1 +mAAgOlyw9D7iZfTxQVB+j8GnIw7kgKSuNelcahV1i8IJH+vo3XyqqVrfnazzlcu9 +RTUMXbPB6wb8zvqcGDLfYLEWOteyVuY5FjIedIW6SMItx46BYNxNgz+zKzXCAORI +ItdnibZs7DC9+cS04uZG7gNTRDfEzhDgh6VxP5EIBFP7lw0KzJWJfdH4jt2seA1U +jS86p2vstqOiHlXROJEAluY1Yk0sbsCofPf12zkQ5cqh5+njPTXCzTOM5oyoA5uh +HPudQzQqOqnfeGTdG56VKQ6MtvHaMGM8pb6L7HRNB3SfrAzIDjir990Ou5vOZ/XQ +zPsH/Cy+IIoZ0gOb0A1dgnhxaKJTAjRmELoecPbb1Lrf3Mtn5hvXgbLOtagxklyv +vosmyY5csbZ+p0GiTswApPrcuaiSjR5+Gyscb43X599IZimwFR+SECdxs+TQ0fkn +olotqe0G12fN2wrSF7pMhEhLWRZzNmsEXJiTxcTlDoCxbVDAdbKaqUZttzUWv5Lu +ycKcHDas2th2eU2iYt4CcX3EUyYSkFo5qAwmvuu/AP45JnV9BxAMR9dwflDTMA8I +e6vtkGlaGL70kZ2JcBPOvX1EqSRCuNRVMZEDcEEG2K4kWBfgsmXXWJzb7jCNninn +ZdxmxFPl7GTSJlSvOG3dtGoDZghxBGNtH8XNGDVkPsWRDVtYvj1byj11IXVTPhvb +U6m//YyT70A5ohFamACZjoyZ+6uOtwkVLvcRQeETkjvNa7M/4pmVrg== +-----END RSA PRIVATE KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private.crt b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private.crt new file mode 100644 index 0000000..9e93597 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOTCCAiECFFgiDO0oHmM+ZmeqQPbpAgTMh6ZbMA0GCSqGSIb3DQEBCwUAMFkx +CzAJBgNVBAYTAklOMQswCQYDVQQIDAJNSDENMAsGA1UEBwwEUHVuZTEOMAwGA1UE +CgwFdGVrZGkxDjAMBgNVBAsMBXRla2RpMQ4wDAYDVQQDDAV0ZWtkaTAeFw0yMzEy +MTIwODQ0MTFaFw0yNDEyMTEwODQ0MTFaMFkxCzAJBgNVBAYTAklOMQswCQYDVQQI +DAJNSDENMAsGA1UEBwwEUHVuZTEOMAwGA1UECgwFdGVrZGkxDjAMBgNVBAsMBXRl +a2RpMQ4wDAYDVQQDDAV0ZWtkaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKimEMO4yhFaR4RXwgoAUWWHZUu0F9WeS9T2SavUOmP3RsmPXkfDEw0wk+Qj +yxUsdUiaNV2fzOWlSrdzjsMuIAzAG+weIYvRfMRmjKErThsRvGSS2qDrWosTFR8I +BUundITQxhU3UX++UMkosEwT9P7HRh1cqTxg5fjx0Mi3hqTLdCAdVrKmRTnUMwCB +3slIC1qEcszDmCck3zp1XZ2eVMI9wrCItojMkcID3t5zoQd0AQaqXG95rrKrOIH9 +VOQl2wC1pnXT7Zouw+iAOWvEqEl3HM8e4xlGbKi7meBUvi/pkeyno8jWl7CaahBA +lF/vl4IV1Dbzqs92l6XMO37kSB8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZ7k/ +3M3YQgM9tXGUmZgtHdoL9Ah5OT5cQmcl/vB7s3B+OfwL0nzN4az62ExGJTaxpNzb +g0pPhaTl6WTNweFsnS0UsZWrYASsyIDo/BvTfmsFwCnEX+AlWc16Ckkma+1rNiYp +9RF2y2xVFTN0AHkjKBNHKMOFozTW10rjPUTXpRQS/hNHKk+92+sfAkBiNUUJcGrq +VVXZ3B+Ltrk/2zNQ3k7djqWtxrd9DVeNHbwr5uauEFVdVmJe00Pg8L0tmUAUiDSs +/vkGNqe9iyZCYnp3DD4VC9NTR62tR6+Su7se6hT+70ViXq47Q2h5grXFSB1UWKLf +PODQCQsPNozrcq/ztQ== +-----END CERTIFICATE----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private.csr b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private.csr new file mode 100644 index 0000000..dac4d74 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/private.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICvDCCAaQCAQAwWTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk1IMQ0wCwYDVQQH +DARQdW5lMQ4wDAYDVQQKDAV0ZWtkaTEOMAwGA1UECwwFdGVrZGkxDjAMBgNVBAMM +BXRla2RpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqKYQw7jKEVpH +hFfCCgBRZYdlS7QX1Z5L1PZJq9Q6Y/dGyY9eR8MTDTCT5CPLFSx1SJo1XZ/M5aVK +t3OOwy4gDMAb7B4hi9F8xGaMoStOGxG8ZJLaoOtaixMVHwgFS6d0hNDGFTdRf75Q +ySiwTBP0/sdGHVypPGDl+PHQyLeGpMt0IB1WsqZFOdQzAIHeyUgLWoRyzMOYJyTf +OnVdnZ5Uwj3CsIi2iMyRwgPe3nOhB3QBBqpcb3musqs4gf1U5CXbALWmddPtmi7D +6IA5a8SoSXcczx7jGUZsqLuZ4FS+L+mR7KejyNaXsJpqEECUX++XghXUNvOqz3aX +pcw7fuRIHwIDAQABoB4wHAYJKoZIhvcNAQkHMQ8MDWZhcm1lcl9zZWFyY2gwDQYJ +KoZIhvcNAQELBQADggEBAHW5Y8jKiRD4tLJA0VTd81zTUge+sBe1G/VFr/4qvyHV +lJ9AdqPXybrD6uPw5tNCYyz7kRikYj6Iy2I3/l0bxbQtgNh1KR+qu0Wti1wgtEdT +jlnfZo/Jn5XsMYqR1BFaUxCODh95edUKRSAUONMkWa+9meNWVSkrb/dUQr650Hhi +QILtNqczSZKtEYfv7z52eEtm5aYaoMLrj4LB4iJXkPu4+hzGycOqVGyDJbr/UHnB +ZP0c+MC9MCO4HYUTxKuf7nW5L25N+laenWpKT/OaYMHukGvCCC3BwRWC7O6bZHZN +RWJodVpkGMKFzZ3bLeQw8rUyi66nkk7JxWXB8vEqJQs= +-----END CERTIFICATE REQUEST----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/public-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/public-key.pem new file mode 100644 index 0000000..a66a6ef --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctofarmer/public-key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqKYQw7jKEVpHhFfCCgBR +ZYdlS7QX1Z5L1PZJq9Q6Y/dGyY9eR8MTDTCT5CPLFSx1SJo1XZ/M5aVKt3OOwy4g +DMAb7B4hi9F8xGaMoStOGxG8ZJLaoOtaixMVHwgFS6d0hNDGFTdRf75QySiwTBP0 +/sdGHVypPGDl+PHQyLeGpMt0IB1WsqZFOdQzAIHeyUgLWoRyzMOYJyTfOnVdnZ5U +wj3CsIi2iMyRwgPe3nOhB3QBBqpcb3musqs4gf1U5CXbALWmddPtmi7D6IA5a8So +SXcczx7jGUZsqLuZ4FS+L+mR7KejyNaXsJpqEECUX++XghXUNvOqz3aXpcw7fuRI +HwIDAQAB +-----END PUBLIC KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/mobile_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/mobile_search.p12 new file mode 100644 index 0000000..372e8c6 Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/mobile_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private-key.pem new file mode 100644 index 0000000..9464a41 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private-key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,C62382ECC7C02A70 + +uIlXPPB9YWzUkGvyhfoiRiZzo+d9dRZrZNfaE+pLruXwaza63XDWA2tB3wcSc5v7 +c9K9Qe/XmCcRoqf6psIehOrJA24x2EmAgIOTad8rQN5ovCU0wdUgoP8HqinJDEwN +tHKNiitmBx42gGMKBAxbHs7nNUIwYxwQmbPX8T5Qif/G8rV44P1tOX6VUnMQ2gkJ +sBv12HJzQPAfOmqqqWQfzcHcjseOlhNEOkvnu2IMMxjojoBmp9pnGFFmucuqdUgt ++UY5/8geLuDFTCGEItK+uTOO5CqHpEeqdL9C714Y79XJShOOqYFX/L24m35CJjFc +UeMBvQERTWWmkW97iw6P40WEm55YEqN4UOfUG0WF8NycbtvUEa5krxUN0f2uyVir +XG0D2Dbz5l8J4TMmEV07COP5HFsy4y4AG/dGPBm5IdzEM4aFd1F4RrmlXZ0joVP3 +LLf4GkyJquQJqOsstLR5OHwIbAojUYIp8asHXBjKQAdHx3SEtpBI3YkQgFOTzgQA +gXAzIG9tdShQUkxEHjGowZ9sKKc6YhkAypt5a/RhCWpyRvbj3uYe/wH6IrqYZjuF +sJoAE35ggiJw2B+FM6yBQgkMUdi9Z4vKdgEBlQhJ9wMOhul/enAtZi6n3jpyhrrX +jpJUL91nCnwn5hoCuK799KF6S7QbTnlDZ/fHWGLkNZ9FwNdbW3dbdxjs34eOM9ON +C5UCGc2LBN2/rj0iCLK+5LfbbYISjeV5aUKjmiQBbfUvGMjU9/tNRxHrr5IJMRZ5 +8b6OwsjBdI6y43+Dix+l1I3giKW72hsbP1zWmkW50QwgwQu130IuCOEBuESHngEs +flQB4Hd99NTd46Fd+t8o932zquJQDtUZmWlk8y3DSzhHAA7QAiIY+XvMtcWPM4V7 +7kz/hd5fZe2tllXVz08T7rWgP+t6/gW/RmLJA4wjgZOYMBA0YTfY7uwHPL/kC9T/ +5hYo6DHNKq+wy8NKzknY6B9Y+J37t+Drr6cNzQ+EWG5oM1VJPKAwpu/GTf6TLJht +o76QTUQLhqe0ILV0r/szL4JMoqaiSNq2rS9poMMl8TCkGkNyV/IQ152egmjtkHj1 +N7vYOCXe53MlJnOPIqmgzLQH/3aBaY2A2iJFTxguDGf40hMDGugUJipe8kSliUJ9 +g1FrUZL6sycnpg5mvhNmxw4K/mE0SR6hea30QlwcE6FjbhrlQa68WqoerL1pcgkD +raGfvy+DHu0WDr3B9E5JEp4Ewo/krL715mP/Tfp2PhtCQ48DQGTkmDAUZ3M1BknS +dQUtB0w2eCLoIK+udzKZYdVPwTEM1R90QdHuU8zyUGjsyyETJjZsOH82/q85Gudl +Z9goag+J+9SF3ygYAc3rcACqm1AUKpUZlj05+c00/JlLgH3S7D3NFAVPgV3XgNbY +hWE9n3kdFI4NMCJABUvuOpgZr7poBruaIJv/05dJ+OAUjnLB2i1u96EbfP60H0AU +/7sOZ/g42GTmghSTI4juphejxBByIqtpdqBDOD0tARzbvzx+CEh5Bd0781ws/l1N +h+QkE2CTLBfECprV1jcC/9xMTSiK2KRmCY6JPl/jItJNjQt71Ooh7g== +-----END RSA PRIVATE KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private.crt b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private.crt new file mode 100644 index 0000000..ce6e43a --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+TCCAeECFAWDmxnPiPlFVZjueTpP+7b7nIXAMA0GCSqGSIb3DQEBCwUAMDkx +CzAJBgNVBAYTAklOMQswCQYDVQQIDAJNSDENMAsGA1UEBwwEUHVuZTEOMAwGA1UE +CgwFdGVrZGkwHhcNMjMxMjEyMDg1MDQwWhcNMjQxMjExMDg1MDQwWjA5MQswCQYD +VQQGEwJJTjELMAkGA1UECAwCTUgxDTALBgNVBAcMBFB1bmUxDjAMBgNVBAoMBXRl +a2RpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqm3yRXloOhZgSgTy +/2d3mtklyr6sAzjHonoVZqZAASV/CvVtw4gWYkXMPM8k4U7n7LoeITGniWlL9oP/ +eJpBZpbcWh8YChJjLGZKuFhfdIRAHh0YEQSLQ90j9XkN2Z7wfNEaCfnacs21/zWB +VdA+jmLoVWOeR+Ewpi76v7NmEmAThptMRlj5qX8K+OzyJ4WrRhu2z63lJep2+gMr +iTO4BwWG8YhtSi8NBzLcs0fceUSdhQktEw++XJY2vwAFInNXCDig4Nlc7oVZlRyf +2/25aUKnflw1V1t64m3i2OskuOfVLm0tH+y2bjpBHDfOfl7RDg5tBcCaWmc12QoQ +9Gnu+wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmuYAYZ75LqsG7/ezFgk5BVxiR +ihF5oHMC8n4ySm2Jcx3ONUEgwQISamzVsUtRz+x7ecUae5z0LrmMus5MXFdskBFG +DifCruqJsUISNy0XCSKINSRwpS22xkQGSpaPqR4aov67JvE3QqYxdCxFQAb6IQeZ +Q0wVGILtqLpdaIRtUihrAcqZHZsLj9idMj9wWajIKZpNnqQyRajJ2IOM+wD9PNMY +emfV8L0/uCT9qkN9LZvgwbRB25S8HEfgN1qhX8VSFIwMFzc3tU9wIS6A9kaosUW/ +FvSpjBrGqzAzkZsofnf4XUAmHQOY6wCxTRqfehW3ve84Dk7Rm5/yCMyDmxk8 +-----END CERTIFICATE----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private.csr b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private.csr new file mode 100644 index 0000000..92f05cc --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/private.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICnDCCAYQCAQAwOTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk1IMQ0wCwYDVQQH +DARQdW5lMQ4wDAYDVQQKDAV0ZWtkaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKpt8kV5aDoWYEoE8v9nd5rZJcq+rAM4x6J6FWamQAElfwr1bcOIFmJF +zDzPJOFO5+y6HiExp4lpS/aD/3iaQWaW3FofGAoSYyxmSrhYX3SEQB4dGBEEi0Pd +I/V5Ddme8HzRGgn52nLNtf81gVXQPo5i6FVjnkfhMKYu+r+zZhJgE4abTEZY+al/ +Cvjs8ieFq0Ybts+t5SXqdvoDK4kzuAcFhvGIbUovDQcy3LNH3HlEnYUJLRMPvlyW +Nr8ABSJzVwg4oODZXO6FWZUcn9v9uWlCp35cNVdbeuJt4tjrJLjn1S5tLR/stm46 +QRw3zn5e0Q4ObQXAmlpnNdkKEPRp7vsCAwEAAaAeMBwGCSqGSIb3DQEJBzEPDA1t +b2JpbGVfc2VhcmNoMA0GCSqGSIb3DQEBCwUAA4IBAQB8F6gXRTL/CVBjmlSK/a1t +zHQjGiFnB3J7cSwWm+es9Nsi069x8tq7P6feroMz9g4feNe0I8o38HwtqsN87XZ4 +G2VGS5iy6HBb+cgYUHT9nJJmRr2CrBQwdae7e/AVzNCW8vhddP8ua2Au9goWl47w +VTmjJpJQSuUk3niv4FSRS0rddPPSEx5Ingjg4nQNyGSR4baICYC23RT+p7+oYY9l +GgMeAVAoiT830ecVAgnC5htoaf6E733DBT9n6kzIy+SpQIHk4WbBXMh1uauJPW0S +JhhLrUQ+mL34T0MeM1TQGTTUkaoM8r4W0w7PhPB1ZoJwXT+68pki0lEku6DzX1MZ +-----END CERTIFICATE REQUEST----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/public-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/public-key.pem new file mode 100644 index 0000000..322245d --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/dctomobile/public-key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqm3yRXloOhZgSgTy/2d3 +mtklyr6sAzjHonoVZqZAASV/CvVtw4gWYkXMPM8k4U7n7LoeITGniWlL9oP/eJpB +ZpbcWh8YChJjLGZKuFhfdIRAHh0YEQSLQ90j9XkN2Z7wfNEaCfnacs21/zWBVdA+ +jmLoVWOeR+Ewpi76v7NmEmAThptMRlj5qX8K+OzyJ4WrRhu2z63lJep2+gMriTO4 +BwWG8YhtSi8NBzLcs0fceUSdhQktEw++XJY2vwAFInNXCDig4Nlc7oVZlRyf2/25 +aUKnflw1V1t64m3i2OskuOfVLm0tH+y2bjpBHDfOfl7RDg5tBcCaWmc12QoQ9Gnu ++wIDAQAB +-----END PUBLIC KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/farmer_on_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/farmer_on_search.p12 new file mode 100644 index 0000000..9a83fcb Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/farmer_on_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private-key.pem new file mode 100644 index 0000000..8ae3b08 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private-key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,B138F6DC5A16BD51 + +NH8voiaeQHTJ/QvtVCXv/0cas0/hSymPWdP8mWkyEfpGx2DTCdLF+RTC6vpnoc1L +jSl9/PqDf8dedkysf9DHeGGg6ygjf7l4h5mH9Orm1JHF+41d2yEpNy9nQs8yCriA +OB1IOT2que6lWM4E7T78mpUro4SCXSExMmKDLaDllCwfYjtP12a6SCIgf/j1FpKy +Fmre6mr8t0QW5rlx8QVi/00OWrcnfUhfT05tofp7eaJA2EMDMU+lB8mKra0ALiIe +3+M31zAfsRLJqyIQuWah2zpuT5sO1srOPxtBhV/0E5wedJAtnSUlpL6mNhWhKo72 +teVbm9uSXA7dUHBp6kfUC1X4Whf9mV2LMfVttWcqTwlkMOBOzGReIiRrAO575zQ7 +wtaYEEt2DskzTacitBXEI9JPWiJUQ7ojGDKpDGoEfdShaoLy+8RxFeHXcBlGlkbk +IKlRPX/c8lU2jfC65if21z3FWWMZIIB3wD6FNus6aLTM3zxbOXCOZ5qSDo6UeT2p +EaG0AFBBXnDZvR+/V6iMprpG19eRCz2cgmvpJbnDTNzkQwTai9fAVjj2hlERjAB5 +YIBgyRlrsEVEiZNXyaMIUp4NWa2yrFGRxQFp6vniV3q4GY3c4vhJ/6DGmYI+d/BL +gI8/V29xZQ9DFgORuxkQky3v6vbkkFAKmWL32wKsOu3TzM+zQkcVjDmrKAP5XZNi +rNHJYA8wrNSVkQ54f9Gyy483VegxAEK8iKmMviuNAJX4+yWuxLP4Wh8drhH/1K63 +xtkMa2e0drZQZkH1EJBtqUedw6bOAJjrYewb0Fod3KxG8KTjNJqMLLZ7G2HAihbY +jbWnVu6YIv2K2iXH021SYoKOpLMijb0oRNtWTAReGwWAz4V6kup4Er6W1p3OkaHC +pGFuqozRd3gbKnjqst5H/SNg92+YJI1xdGl1JdQDsSFbsVu5QFI+1FLxL43cKjOR +A4S6agUPeg7f9GS1ruTtMpQ74jfS6f0VKTETeaBci6rlcouWiSFrdrBdNaOiIEhh +JIFeJG3HFSeWFp3DifkdsN/qZ2PPW4NS0Sego0V0AVpAAsGQlp1VUPa/YoNmYRfY +r1QxvDy5BP+Wq2IhOA3XaIwpSa5hfbtLoXAlsxco8YxoDBoB577tPha5xrHtQAvt +cZwgFNnkHyFm6r/C2xWowzLGJcfrmVehvmrJ7hKlRPp5X3fe1SDhand+czpLnYXl +0zkrYBPvpy7okHi8uf4zc2woYCtfPnQmMcQNi+v1Z4LUxtd7BFNRpTyt7R8UK342 +mU7LgshUCLr5mTxcHXDQtJ5JQlQiWvdXW5tvznpyI1L1kh3xKrFklX/XZ/OsHs3S +obcXkYrsrQ3TRpBkXVSVXGB11loqI7f12rg91VZa28cqUBvcFJJvoEtxE5xwCl4N +6dkQ2RU4NbnXobc/Gghoibxh2f4fajyXynlQCLXZyHPGpmsjoUTpG7YShpPuf/h5 +Lkea2MAcY3NkhkFf6fPOURjQzAYCl66ofB47RK+7jxJkLY8oD3df3ZpPkeq+ZnIb +LBBT7x/cB42TCYRcJ0vDBRcRaPkHE7TZD6Ux86rLqmIG8E7V/nUyDg== +-----END RSA PRIVATE KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private.crt b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private.crt new file mode 100644 index 0000000..09da22f --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGTCCAgECFBg5xnLIBS/v50Tomil7QnEPtmkNMA0GCSqGSIb3DQEBCwUAMEkx +CzAJBgNVBAYTAklOMQswCQYDVQQIDAJNSDENMAsGA1UEBwwEUHVuZTEOMAwGA1UE +CgwFdGVrZGkxDjAMBgNVBAsMBXRla2RpMB4XDTIzMTIxMjA4NTgyNloXDTI0MTIx +MTA4NTgyNlowSTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk1IMQ0wCwYDVQQHDARQ +dW5lMQ4wDAYDVQQKDAV0ZWtkaTEOMAwGA1UECwwFdGVrZGkwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDM4EmZs820e8DioL/nAV7mrrzzIZIa+fQCo/kR +5znNp2FL+5LO1bbNAy6s/5jBEnYUVXAVsfAE3Qt+gIO90Jl2kdpvx4JD8GKqwGS0 +4EZUBXAQRYAFW5hHzZsRQZPKFKWK7Dis1J2fZ8KMVoGMnFceSmj2vmAsgdoIl3s7 +fMcboKtnb0+0yi8ZC87fNAGZ8otcXorSY3CzLiLg3u17s7/UrZ8FnvkQ0yMzCwUw +Cq0TYQusJ8mLzbzXQlsAA+qFPOl+MrWp95iVFGM97ZC5I+8Kj6gMoMhUyr3sWPTV +fRLNY1wce4rR8lJ4TsosqdHVJrTgd1m5RdntaO3lVayYUfyxAgMBAAEwDQYJKoZI +hvcNAQELBQADggEBAKlfm7uoO61/8iePiZNpgUMvVtn604Cluzx3x/w0GBoDp1qK +6tfkgjPeRZL6WJW5kQsKuhMfckNvdCwU0sacQEflydldpQR316ssfxeeqcokDh0S +SorJKbfsWaiVTmmxn53mSps5kduDhrzAeTyPkL4Oks4rlo0BoBdVkrNg7mpH7dCd +g1b3VGuFSiQWWqoCEhLpFEF/4s44Cj8JJemfcf6RXKfHDnylOMHEAM3fsd2D2STl +29mv2Ze9ouBBJqpa40HR60S/chEgTP2hfoudje6xpkEhWjzwG13pGCwCKA0YZvUK +lLWFUJYfxr0E0UvYpIHrKgy3rmO7RXxY4HqMru8= +-----END CERTIFICATE----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private.csr b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private.csr new file mode 100644 index 0000000..f21d893 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/private.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICrzCCAZcCAQAwSTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk1IMQ0wCwYDVQQH +DARQdW5lMQ4wDAYDVQQKDAV0ZWtkaTEOMAwGA1UECwwFdGVrZGkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDM4EmZs820e8DioL/nAV7mrrzzIZIa+fQC +o/kR5znNp2FL+5LO1bbNAy6s/5jBEnYUVXAVsfAE3Qt+gIO90Jl2kdpvx4JD8GKq +wGS04EZUBXAQRYAFW5hHzZsRQZPKFKWK7Dis1J2fZ8KMVoGMnFceSmj2vmAsgdoI +l3s7fMcboKtnb0+0yi8ZC87fNAGZ8otcXorSY3CzLiLg3u17s7/UrZ8FnvkQ0yMz +CwUwCq0TYQusJ8mLzbzXQlsAA+qFPOl+MrWp95iVFGM97ZC5I+8Kj6gMoMhUyr3s +WPTVfRLNY1wce4rR8lJ4TsosqdHVJrTgd1m5RdntaO3lVayYUfyxAgMBAAGgITAf +BgkqhkiG9w0BCQcxEgwQZmFybWVyX29uX3NlYXJjaDANBgkqhkiG9w0BAQsFAAOC +AQEAD2Hah9cJwoNoDryZG6dtXmaJ05ZYLuskgH3zntbHpOzD+LoqDZmy07YcQa+f +X4N4qPx9YW5Z8qOglqjgM4ww+ohMTDghjKlBu390Yxc0IMot5mBQV/U4q+Qijztw +was/KoaHpMvCWlDK3NIcMykPbhkaASfAf1slc4EbPXAN0RgDEOanHkO9xxz2fra+ +YY+sFnkNvJEAjpQOC1Sfwmz1p9qmQU+GLugq/GsgSaFDhyAXQZnJdb5SQzVkshUt +GCzVmxD9dFaXuOKYhBCkr34WvM/zLUrf/qWsJGlmgdVjmaAyPv+N/Ua5JXYIW+Sh +TpEUQl//nYglp2HJRIAJBMyHhA== +-----END CERTIFICATE REQUEST----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/public-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/public-key.pem new file mode 100644 index 0000000..ab1b37a --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/farmertodc/public-key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzOBJmbPNtHvA4qC/5wFe +5q688yGSGvn0AqP5Eec5zadhS/uSztW2zQMurP+YwRJ2FFVwFbHwBN0LfoCDvdCZ +dpHab8eCQ/BiqsBktOBGVAVwEEWABVuYR82bEUGTyhSliuw4rNSdn2fCjFaBjJxX +Hkpo9r5gLIHaCJd7O3zHG6CrZ29PtMovGQvO3zQBmfKLXF6K0mNwsy4i4N7te7O/ +1K2fBZ75ENMjMwsFMAqtE2ELrCfJi82810JbAAPqhTzpfjK1qfeYlRRjPe2QuSPv +Co+oDKDIVMq97Fj01X0SzWNcHHuK0fJSeE7KLKnR1Sa04HdZuUXZ7Wjt5VWsmFH8 +sQIDAQAB +-----END PUBLIC KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/mobile_on_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/mobile_on_search.p12 new file mode 100644 index 0000000..43fe9ea Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/mobile_on_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private-key.pem new file mode 100644 index 0000000..03b8c76 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private-key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,AA508A205AF0D834 + +uQz5g95g2/fTVLA14D9IyymD2KEs/smmh5cP5F4PpUMIKh+qeUxkp5yG/C/x2sSr +ilcEMHI/4w50mzIWUOGhwZwojjXgJDq2srJcD2o/dI4YWUWy6GRqZ1OmKePhUzRY +ffT4H61uK9BIYhvis3tLG4z7z6ISB8LiTMBjCkd/RErSfPr1JhDqZebuvj67E3wM +AD2i5VIR97IEPfkUAQjKKNQ/9kdjV7JXXglZLWkqLXzEhe1xeg/TaE439lCzpMBY +9S/jKsbKaipnVJtbxvUM8ZYGvmsXD8rmXKPpYFAPwBnknqhkR1r4w2lCcc7oEQII +OdVEFChVViWXnvTfvtKYwuGEIpRCU2oO9nLiSPCpqFVKfC3a01pzBFHFCMKmUeUI +YDD4XuKjMyvjlxnrVdve7Zo5FdZK08Y5UKQYbOP6QcptX5iDrBPMhZSdZeRQaM6U +0Pveld/YJE2hyPCnJW6U37QY8jq9XjFLsQnLWzoyRMRafJucXPwo3s8NEFDP0rR1 +LCUMqqXjzxztjb/g+jL4o3GwpcpI2N1PLdKd3IXcWWinVlkg5PI6tF3Sx/aWC5aS +K38FzkPhUj2Mc4873+iacLUgkAjt6FdYf6lrLM311X+P87P3kk8T/f+Q7SH2vUDq +DvSR/8kTPkEH4tihzc9AAFgFoT6VKJKjS4P8hbL85o4lrLvJ4P2I2zgqkM5X4xPO +4BblMEvjtk3J1qJspj9bBK23R92msQnfhw4hG6Clrwii63BawuDa+i+Lw5ynj4zZ +4379N+cmAyy73GpEhEiek/3lvTx7/ogy50912xoADkM7Qhe5G2PHZJmlXDyXvvPd +vDT2lDSIYegAcBnEhZ+ytobIW5/fLAlfN5h2cG7ZJ573wiDDNkgfLXK1US+Fj0bw +/WEGVuZrVJIs8sd4n2yXAs7HBiM7jMDrGYv+sHJBvKIDd4v1OtfX4ojQZIvT6r/v +rot1grpBDcytz70X0AkMS7eu7S1NLkWEIaXxv6vn1JWVkkOHWBBRl/ShOO2Y1i8S +jfwaHuaQUglkPu7jfwlLhdlGba+reh/0Uigo7LrAaM4aQLVt4QTE0Np2RbYWAg4n +/eglsLZBVORAZdK9zO7tRgxI5OL+fLJ0J/iKhRN+IfKTNYsFbTOGvADa2Q6WdD1/ +O9kLKaO1u9S5rdOBs4goYjXZnezKM6GlXiXBsWLrLugAJvO1LZUy9Fo6XsiUyjIM +VC+j+WteMd1yEdeBvagLU0F4q2Y0vENqE0TAfzYQ8JfGhFT3SbcDmA3eMSegUWGM +R9XSpoNtralwbVMS66y4AvhKscwoEZRj0yTJgZaM7b1zzK65Ii71UZRRf8ZusjMW +cyQ6yA2udwmVLMNbrNLywQKZBEaHpPmjjH7j/taDP+xw01Ws2uhbgV6Le8WKIJ3W +gYjetyWpIvOsrYus+6MCm/V9JiHxn4qf6puUkJ80oz4kRjOStcU3O4G6stExagyZ +ydUsFMn1pDlMzLGXNuZLmPzkvsmttnJ8l+EUfNVQ3LwaHXh3P6xOW6ksyxw+bBk4 +fL1S8pDHu/KzF/kTQbNstNCJGPYuUr5WIqGOMMowIFR95bc/avCrNXGTUnl3e3RN +-----END RSA PRIVATE KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private.crt b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private.crt new file mode 100644 index 0000000..08a2dd4 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+TCCAeECFGLMqo7TD2wcLLrEP+cN+D/XmZ5kMA0GCSqGSIb3DQEBCwUAMDkx +CzAJBgNVBAYTAklOMQswCQYDVQQIDAJNSDENMAsGA1UEBwwEUHVuZTEOMAwGA1UE +CgwFdGVrZGkwHhcNMjMxMjEyMDkwNTAwWhcNMjQxMjExMDkwNTAwWjA5MQswCQYD +VQQGEwJJTjELMAkGA1UECAwCTUgxDTALBgNVBAcMBFB1bmUxDjAMBgNVBAoMBXRl +a2RpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApD0NQGQilYe8ykZO +FaHWgzVFD73UuLpWiMPBh0gDKY6MmrznJ+H+WtWTIHwSTphjLMIq41s5v/h/EaPx +hhH1c4hegfzxg7bgg5sLaWJhlXyNQJnQcNuXR5xl/TsPl8Kl8T1ttU2++jpmKNRm +SSb6f6IkVf7b5LJnef4G6gJxpfAkWEGNNIr9kjuyqdavGtmHFPeVKJlP5vUjJ4hU +M5FtzEEeRV0asAMTGSM0BFoauOfMnfJg9hHSoq+623aBz56BeGqpuMXaBUEOi4AD +awPVWoyy8vkiuIBviS06dmm2FhLTt7FpiRV4Fj2ltl6HzeIn9RnGZz7zLASBFmbD +dWOJpQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAQsNzBb6HRsc79nlFlmqcpuIPG +Oz+J4+ZY4XYLvAbyjCrWSnERwka0hgqfJmYADFv1qogtKSg9+82nNprqiA3RGexE +uGrmLTUYrQmLONGgUHtpaUoenuGDVtJdraBrC9jO6yQ478yQzzEDopmIqxAt/bfc +hRGuT52vtnVQJhjO7/3c/z7jTdMxoIHEFkohwA+vOINOp7kSn0SWTEnMQoo7LNR1 +DSyAlntgcrxc/JxZpVrLHwSgaua0v8GXzcR1ij1d0reol92tXGLldGQrYTFgsXKw +r8jVMR1WJsHrw31Ud9lpeGs1LzzkVvzlb4CHbifsCfoc6RflI3/Im0IwFH50 +-----END CERTIFICATE----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private.csr b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private.csr new file mode 100644 index 0000000..258e4a2 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/private.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICnzCCAYcCAQAwOTELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAk1IMQ0wCwYDVQQH +DARQdW5lMQ4wDAYDVQQKDAV0ZWtkaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKQ9DUBkIpWHvMpGThWh1oM1RQ+91Li6VojDwYdIAymOjJq85yfh/lrV +kyB8Ek6YYyzCKuNbOb/4fxGj8YYR9XOIXoH88YO24IObC2liYZV8jUCZ0HDbl0ec +Zf07D5fCpfE9bbVNvvo6ZijUZkkm+n+iJFX+2+SyZ3n+BuoCcaXwJFhBjTSK/ZI7 +sqnWrxrZhxT3lSiZT+b1IyeIVDORbcxBHkVdGrADExkjNARaGrjnzJ3yYPYR0qKv +utt2gc+egXhqqbjF2gVBDouAA2sD1VqMsvL5IriAb4ktOnZpthYS07exaYkVeBY9 +pbZeh83iJ/UZxmc+8ywEgRZmw3VjiaUCAwEAAaAhMB8GCSqGSIb3DQEJBzESDBBt +b2JpbGVfb25fc2VhcmNoMA0GCSqGSIb3DQEBCwUAA4IBAQCa0VDxdoo8UPnhWwEy +5o+CzJU1LDIyng9AF2iPpstwhflNlkswTjyJsFw0w/PztSfIedlJedyCakEnSWlh +e3Q4WgwhFToBc+PH0EdEsKop8MWiVkGDj43YvWOyvrZP5wx6e6OlQEOuOgFrmpQZ +rfu4r63fv2gjLKQzV5Z4uLF6cVfSDS22V29tK3P5nb9nrlmYFIc32Fu+dyn9//BM +U0VmnjUzvGzpBrGDnkpAafkZZZyURRZFXz6QvXYAUJ1v4HRVCszpEVUTSTWoDR7X +uns0C4fwwzQkr/Can/TNk+OLu1Pcf/GETlfFSAWnhwS+WOJtvN/94+1/RKRTuEYG +CMB+ +-----END CERTIFICATE REQUEST----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/public-key.pem b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/public-key.pem new file mode 100644 index 0000000..1869b2b --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/security/mobiletodc/public-key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApD0NQGQilYe8ykZOFaHW +gzVFD73UuLpWiMPBh0gDKY6MmrznJ+H+WtWTIHwSTphjLMIq41s5v/h/EaPxhhH1 +c4hegfzxg7bgg5sLaWJhlXyNQJnQcNuXR5xl/TsPl8Kl8T1ttU2++jpmKNRmSSb6 +f6IkVf7b5LJnef4G6gJxpfAkWEGNNIr9kjuyqdavGtmHFPeVKJlP5vUjJ4hUM5Ft +zEEeRV0asAMTGSM0BFoauOfMnfJg9hHSoq+623aBz56BeGqpuMXaBUEOi4ADawPV +Woyy8vkiuIBviS06dmm2FhLTt7FpiRV4Fj2ltl6HzeIn9RnGZz7zLASBFmbDdWOJ +pQIDAQAB +-----END PUBLIC KEY----- diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcRequestBuilderService.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcRequestBuilderService.java new file mode 100644 index 0000000..ba78155 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcRequestBuilderService.java @@ -0,0 +1,18 @@ +package g2pc.ref.dc.client.service; + +import g2pc.core.lib.dto.common.AcknowledgementDTO; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public interface DcRequestBuilderService { + + AcknowledgementDTO generateRequest(List> payloadMapList, String protocol, + String isSignEncrypt, String payloadFilename, String inboundFilename) throws Exception; + + AcknowledgementDTO generateStatusRequest(String transactionID, String transactionType, String protocol) throws Exception; + + String demoTestEncryptionSignature(File payloadFile) throws IOException; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcResponseHandlerService.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcResponseHandlerService.java new file mode 100644 index 0000000..5f4c813 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcResponseHandlerService.java @@ -0,0 +1,16 @@ +package g2pc.ref.dc.client.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.exceptions.G2pHttpException; + +import java.io.IOException; + +public interface DcResponseHandlerService { + + AcknowledgementDTO getResponse(ResponseDTO responseDTO, String outboundFilename, Boolean sunbirdEnabled) throws IOException, G2pHttpException; + + AcknowledgementDTO getStatusResponse(StatusResponseDTO statusResponseDTO) throws IOException, G2pHttpException; +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcSftpDataService.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcSftpDataService.java new file mode 100644 index 0000000..e2d5a1b --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcSftpDataService.java @@ -0,0 +1,4 @@ +package g2pc.ref.dc.client.service; + +public interface DcSftpDataService { +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcSftpPushUpdateService.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcSftpPushUpdateService.java new file mode 100644 index 0000000..eecbdcf --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcSftpPushUpdateService.java @@ -0,0 +1,10 @@ +package g2pc.ref.dc.client.service; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +public interface DcSftpPushUpdateService { + + SseEmitter register(); + + void pushUpdate(Object update); +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcValidationService.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcValidationService.java new file mode 100644 index 0000000..35320b2 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/service/DcValidationService.java @@ -0,0 +1,22 @@ +package g2pc.ref.dc.client.service; + +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseMessageDTO; +import g2pc.core.lib.exceptions.G2pcValidationException; +import org.springframework.stereotype.Service; +import java.io.IOException; +import java.util.Map; +@Service +public interface DcValidationService { + + public void validateResponseDto(ResponseDTO responseDTO) throws Exception; + + ResponseMessageDTO signatureValidation(Map metaData, ResponseDTO responseDTO) throws Exception; + + StatusResponseMessageDTO signatureValidation(Map metaData, StatusResponseDTO statusResponseDTO) throws Exception; + + void validateStatusResponseDTO(StatusResponseDTO statusResponseDTO) throws IOException, G2pcValidationException; +} + diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcRequestBuilderServiceImpl.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcRequestBuilderServiceImpl.java new file mode 100644 index 0000000..6a27d11 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcRequestBuilderServiceImpl.java @@ -0,0 +1,228 @@ +package g2pc.ref.dc.client.serviceimpl; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.constants.SftpConstants; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.search.message.request.SearchCriteriaDTO; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.dto.status.message.request.TxnStatusRequestDTO; +import g2pc.core.lib.enums.ActionsENUM; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.service.ElasticsearchService; +import g2pc.core.lib.utils.CommonUtils; +import g2pc.dc.core.lib.constants.DcConstants; +import g2pc.dc.core.lib.dto.ResponseTrackerDto; +import g2pc.dc.core.lib.entity.ResponseTrackerEntity; +import g2pc.dc.core.lib.repository.ResponseTrackerRepository; +import g2pc.dc.core.lib.service.RequestBuilderService; +import g2pc.dc.core.lib.service.TxnTrackerService; +import g2pc.ref.dc.client.config.RegistryConfig; +import g2pc.ref.dc.client.constants.Constants; +import g2pc.ref.dc.client.service.DcRequestBuilderService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.elasticsearch.action.search.SearchResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; +import org.springframework.core.io.Resource; + +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class DcRequestBuilderServiceImpl implements DcRequestBuilderService { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Autowired + private RequestBuilderService requestBuilderService; + + @Autowired + RegistryConfig registryConfig; + + @Autowired + TxnTrackerService txnTrackerService; + + @Autowired + private ResourceLoader resourceLoader; + + @Autowired + ResponseTrackerRepository responseTrackerRepository; + + @Autowired + private ElasticsearchService elasticsearchService; + + /** + * Create, save and send a request from payload + * + * @param payloadMapList required query params data + * @return acknowledgement of the request + */ + @SuppressWarnings("unchecked") + @Override + public AcknowledgementDTO generateRequest(List> payloadMapList, String protocol, + String isSignEncrypt, String payloadFilename, String inboundFilename) throws Exception { + Map g2pcErrorMap = new HashMap<>(); + + List> queryMapList = requestBuilderService.createQueryMap(payloadMapList, registryConfig.getQueryParamsConfig().entrySet()); + + for (Map.Entry configEntryMap : registryConfig.getRegistrySpecificConfig(isSignEncrypt).entrySet()) { + + List> queryMapFilteredList = queryMapList.stream() + .map(map -> map.entrySet().stream() + .filter(entry -> entry.getKey().equals(configEntryMap.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))).toList(); + + Map registrySpecificConfigMap = (Map) registryConfig.getRegistrySpecificConfig(isSignEncrypt).get(configEntryMap.getKey()); + List searchCriteriaDTOList = new ArrayList<>(); + for (Map queryParamsMap : queryMapFilteredList) { + SearchCriteriaDTO searchCriteriaDTO = requestBuilderService.getSearchCriteriaDTO(queryParamsMap, registrySpecificConfigMap); + searchCriteriaDTOList.add(searchCriteriaDTO); + } + + String transactionId = CommonUtils.generateUniqueId("T"); + String requestString = requestBuilderService.buildRequest(searchCriteriaDTOList, transactionId, ActionsENUM.SEARCH); + String encryptedSalt = ""; + G2pcError g2pcError = new G2pcError(); + switch (isSignEncrypt) { + case "0": + break; + case "1": + encryptedSalt = "salt"; + case "2": + break; + } + try { + if (protocol.equals(CoreConstants.SEND_PROTOCOL_HTTPS)) { + Resource resource = resourceLoader.getResource(registrySpecificConfigMap.get(CoreConstants.KEY_PATH).toString()); + + InputStream fis = resource.getInputStream(); + g2pcError = requestBuilderService.sendRequest(requestString, + registrySpecificConfigMap.get(CoreConstants.DP_SEARCH_URL).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_ID).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_SECRET).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_URL).toString(), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_ENCRYPTION).toString()), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_SIGNATURE).toString()), + fis, encryptedSalt, + registrySpecificConfigMap.get(CoreConstants.KEY_PASSWORD).toString(), CoreConstants.SEARCH_TXN_TYPE); + g2pcErrorMap.put(configEntryMap.getKey(), g2pcError); + log.info("DP_SEARCH_URL = {}", registrySpecificConfigMap.get(CoreConstants.DP_SEARCH_URL).toString()); + } else if (protocol.equals(CoreConstants.SEND_PROTOCOL_SFTP)) { + SftpServerConfigDTO sftpServerConfigDTO = new SftpServerConfigDTO(); + sftpServerConfigDTO.setUser(registrySpecificConfigMap.get(SftpConstants.SFTP_USER).toString()); + sftpServerConfigDTO.setHost(registrySpecificConfigMap.get(SftpConstants.SFTP_HOST).toString()); + sftpServerConfigDTO.setPort(Integer.parseInt(registrySpecificConfigMap.get(SftpConstants.SFTP_PORT).toString())); + sftpServerConfigDTO.setPassword(registrySpecificConfigMap.get(SftpConstants.SFTP_PASSWORD).toString()); + sftpServerConfigDTO.setStrictHostKeyChecking(registrySpecificConfigMap.get(SftpConstants.SFTP_SESSION_CONFIG).toString()); + sftpServerConfigDTO.setRemoteInboundDirectory(registrySpecificConfigMap.get(SftpConstants.SFTP_REMOTE_INBOUND_DIRECTORY).toString()); + + Resource resource = resourceLoader.getResource(registrySpecificConfigMap.get(CoreConstants.KEY_PATH).toString()); + InputStream fis = resource.getInputStream(); + inboundFilename = UUID.randomUUID() + ".json"; + g2pcError = requestBuilderService.sendRequestSftp(requestString, + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_ENCRYPTION).toString()), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_SIGNATURE).toString()), + fis, encryptedSalt, + registrySpecificConfigMap.get(CoreConstants.KEY_PASSWORD).toString(), CoreConstants.SEARCH_TXN_TYPE, + sftpServerConfigDTO, inboundFilename); + g2pcErrorMap.put(configEntryMap.getKey(), g2pcError); + if (g2pcError != null && g2pcError.getCode().contains("err")) { + log.info("Uploaded failed for : {}", registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + throw new Exception("Uploaded failed for : " + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + } else { + log.info("Uploaded to inbound of : {}", registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + } + } + txnTrackerService.saveInitialTransaction(payloadMapList, transactionId, HeaderStatusENUM.RCVD.toValue(), protocol); + txnTrackerService.saveRequestTransaction(requestString, + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString(), transactionId, protocol); + G2pcError g2pcErrorDb= txnTrackerService.saveRequestInDB(requestString, + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString(), protocol, g2pcError, + payloadFilename, inboundFilename,sunbirdEnabled); + g2pcErrorMap.put(configEntryMap.getKey(),g2pcErrorDb); + } catch (Exception e) { + log.error(Constants.GENERATE_REQUEST_ERROR_MESSAGE + ": {}", e.getMessage()); + } + } + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + acknowledgementDTO.setMessage(g2pcErrorMap); + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + return acknowledgementDTO; + } + + @SuppressWarnings("unchecked") + @Override + public AcknowledgementDTO generateStatusRequest(String transactionID, String transactionType, String protocol) throws Exception { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + String statusRequestTransactionId = CommonUtils.generateUniqueId("T"); + ObjectMapper objectMapper = new ObjectMapper(); + String encryptedSalt = ""; + Map fieldValues = new HashMap<>(); + fieldValues.put("transaction_id.keyword",transactionID); + SearchResponse responseTrackerSearchResponse = elasticsearchService.exactSearch("response_tracker", fieldValues); + if (responseTrackerSearchResponse.getHits().getHits().length > 0) { + + String responseTrackerDtoString = responseTrackerSearchResponse.getHits().getHits()[0].getSourceAsString(); + ResponseTrackerDto responseTrackerDto = objectMapper.readerFor(ResponseTrackerDto.class). + readValue(responseTrackerDtoString); + String registryType = responseTrackerDto.getRegistryType().substring(3).toLowerCase(); + Map registrySpecificConfigMap = (Map) registryConfig.getRegistrySpecificConfig("").get(registryType); + TxnStatusRequestDTO txnStatusRequestDTO = requestBuilderService.buildTransactionRequest(transactionID, transactionType); + String statusRequestString = requestBuilderService.buildStatusRequest(txnStatusRequestDTO, statusRequestTransactionId, ActionsENUM.STATUS); + G2pcError g2pcError = null; + try { + Resource resource = resourceLoader.getResource(registrySpecificConfigMap.get(CoreConstants.KEY_PATH).toString()); + InputStream fis = resource.getInputStream(); + g2pcError = requestBuilderService.sendRequest(statusRequestString, + registrySpecificConfigMap.get(CoreConstants.DP_STATUS_URL).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_ID).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_CLIENT_SECRET).toString(), + registrySpecificConfigMap.get(CoreConstants.KEYCLOAK_URL).toString(), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_ENCRYPTION).toString()), + Boolean.parseBoolean(registrySpecificConfigMap.get(CoreConstants.SUPPORT_SIGNATURE).toString()), + fis, encryptedSalt, + registrySpecificConfigMap.get(CoreConstants.KEY_PASSWORD).toString(), "status"); + log.info("" + g2pcError); + } catch (Exception e) { + log.error(Constants.GENERATE_REQUEST_ERROR_MESSAGE, e); + } + txnTrackerService.saveInitialStatusTransaction(transactionType, statusRequestTransactionId, HeaderStatusENUM.RCVD.toValue(), protocol); + txnTrackerService.saveRequestTransaction(statusRequestString, + registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString(), statusRequestTransactionId, protocol); + txnTrackerService.saveRequestInStatusDB(statusRequestString, registrySpecificConfigMap.get(CoreConstants.REG_TYPE).toString()); + acknowledgementDTO.setMessage(g2pcError); + acknowledgementDTO.setStatus(HeaderStatusENUM.RCVD.toValue()); + } else { + G2pcError g2pcError = new G2pcError(); + g2pcError.setCode(ExceptionsENUM.ERROR_REQUEST_NOT_FOUND.toValue()); + g2pcError.setMessage("Data for transaction id " + transactionID + "is not found"); + acknowledgementDTO.setMessage(g2pcError); + } + return acknowledgementDTO; + } + + + public String demoTestEncryptionSignature(File payloadFile) throws IOException { + Reader reader = null; + reader = new BufferedReader(new FileReader(payloadFile)); + CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT); + List csvRecordList = csvParser.getRecords(); + CSVRecord headerRecord = (CSVRecord) csvRecordList.get(0); + + return headerRecord.get(headerRecord.size() - 1); + + } +} + diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcResponseHandlerServiceImpl.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcResponseHandlerServiceImpl.java new file mode 100644 index 0000000..83a400b --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcResponseHandlerServiceImpl.java @@ -0,0 +1,79 @@ +package g2pc.ref.dc.client.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.dc.core.lib.service.TxnTrackerService; +import g2pc.ref.dc.client.constants.Constants; +import g2pc.ref.dc.client.service.DcResponseHandlerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +@Slf4j +public class DcResponseHandlerServiceImpl implements DcResponseHandlerService { + + @Autowired + private TxnTrackerService txnTrackerService; + + /** + * @param responseDTO responseDTO + * @return AcknowledgementDTO + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public AcknowledgementDTO getResponse(ResponseDTO responseDTO, String outboundFilename, Boolean sunbirdEnabled) throws IOException, G2pHttpException { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + + G2pcError g2pcError = txnTrackerService.updateTransactionDbAndCache(responseDTO, outboundFilename, sunbirdEnabled); + log.info("on-search response received from registry : {}", objectMapper.writeValueAsString(responseDTO)); + log.info("on-search database updation response from sunbird - " + g2pcError.getCode()); + if (g2pcError.getCode().equals(HttpStatus.OK.toString())) { + acknowledgementDTO.setMessage(Constants.ON_SEARCH_RESPONSE_RECEIVED.toString()); + acknowledgementDTO.setStatus(Constants.COMPLETED); + + } else { + acknowledgementDTO.setMessage(Constants.INVALID_RESPONSE.toString()); + acknowledgementDTO.setStatus(Constants.PENDING); + throw new G2pHttpException(g2pcError); + + } + + return acknowledgementDTO; + } + + /** + * @param statusResponseDTO statusResponseDTO + * @return AcknowledgementDTO + * @throws JsonProcessingException jsonProcessingException might be thrown + */ + @Override + public AcknowledgementDTO getStatusResponse(StatusResponseDTO statusResponseDTO) throws IOException, G2pHttpException { + AcknowledgementDTO acknowledgementDTO = new AcknowledgementDTO(); + ObjectMapper objectMapper = new ObjectMapper(); + + G2pcError g2pcError = txnTrackerService.updateStatusTransactionDbAndCache(statusResponseDTO); + log.info("on-status response received from registry : {}", objectMapper.writeValueAsString(statusResponseDTO)); + log.info("on-status database updation response from sunbird - " + g2pcError.getCode()); + if (g2pcError.getCode().equals(HttpStatus.OK.toString())) { + acknowledgementDTO.setMessage(Constants.ON_STATUS_RESPONSE_RECEIVED.toString()); + acknowledgementDTO.setStatus(Constants.COMPLETED); + + } else { + acknowledgementDTO.setMessage(Constants.INVALID_RESPONSE.toString()); + acknowledgementDTO.setStatus(Constants.PENDING); + throw new G2pHttpException(g2pcError); + + } + return acknowledgementDTO; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcSftpPushUpdateServiceImpl.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcSftpPushUpdateServiceImpl.java new file mode 100644 index 0000000..d16dbe1 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcSftpPushUpdateServiceImpl.java @@ -0,0 +1,51 @@ +package g2pc.ref.dc.client.serviceimpl; + +import g2pc.core.lib.utils.CommonUtils; +import g2pc.ref.dc.client.dto.dashboard.SftpDcData; +import g2pc.ref.dc.client.service.DcSftpPushUpdateService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class DcSftpPushUpdateServiceImpl implements DcSftpPushUpdateService { + + private final List emitters = new ArrayList<>(); + + public SseEmitter register() { + int minutes = 15; + long timeout = (long) minutes * 60000; + SseEmitter emitter = new SseEmitter(timeout); + this.emitters.add(emitter); + emitter.onCompletion(() -> this.emitters.remove(emitter)); + emitter.onTimeout(() -> this.emitters.remove(emitter)); + log.info("SSE emitter registered" + emitter); + return emitter; + } + + public void pushUpdate(Object update) { + List deadEmitters = new ArrayList<>(); + this.emitters.forEach(emitter -> { + try { + emitter.send(update); + } catch (IOException e) { + deadEmitters.add(emitter); + } + }); + this.emitters.removeAll(deadEmitters); + } + + public SftpDcData buildSftpDcData(String transactionId, String filename) { + SftpDcData sftpDcData = new SftpDcData(); + sftpDcData.setMessageTs(CommonUtils.getCurrentTimeStamp()); + sftpDcData.setTransactionId(transactionId); + sftpDcData.setFileName(filename); + sftpDcData.setSftpDirectoryType("INBOUND"); + return sftpDcData; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcValidationServiceImpl.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcValidationServiceImpl.java new file mode 100644 index 0000000..f3a3d61 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/serviceimpl/DcValidationServiceImpl.java @@ -0,0 +1,354 @@ +package g2pc.ref.dc.client.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.constants.G2pSecurityConstants; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.ResponseMessageDTO; +import g2pc.core.lib.dto.search.message.response.ResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseDTO; +import g2pc.core.lib.dto.status.message.response.StatusResponseMessageDTO; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.core.lib.security.service.AsymmetricSignatureService; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import g2pc.core.lib.security.service.G2pcUtilityClass; +import g2pc.dc.core.lib.service.ResponseHandlerService; +import g2pc.ref.dc.client.constants.Constants; +import g2pc.ref.dc.client.service.DcValidationService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; +import java.io.IOException; +import java.io.InputStream; +import java.security.SignatureException; +import java.util.*; + + +@Service +@Slf4j +public class DcValidationServiceImpl implements DcValidationService { + + @Autowired + ResponseHandlerService responseHandlerService; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Autowired + G2pcUtilityClass g2pcUtilityClass; + + @Autowired + private AsymmetricSignatureService asymmetricSignatureService; + + @Autowired + private ResourceLoader resourceLoader; + + @Value("${crypto.from_dp_farmer.support_encryption}") + private boolean isFarmerEncrypt; + + @Value("${crypto.from_dp_farmer.support_signature}") + private boolean isFarmerSign; + + @Value("${crypto.from_dp_farmer.password}") + private String farmerp12Password; + + @Value("${crypto.from_dp_farmer.key_path}") + private String farmerKeyPath; + + @Value("${crypto.from_dp_farmer.id}") + private String farmerID; + + @Value("${crypto.from_dp_mobile.support_encryption}") + private boolean isMobileEncrypt; + + @Value("${crypto.from_dp_mobile.support_signature}") + private boolean isMobileSign; + + @Value("${crypto.from_dp_mobile.password}") + private String mobilep12Password; + + @Value("${crypto.from_dp_mobile.key_path}") + private String mobileKeyPath; + + @Value("${crypto.from_dp_mobile.id}") + private String mobileID; + + /** + * Validate response dto. + * + * @param responseDTO the response dto + * @throws G2pcValidationException the g 2 pc validation exception + * @throws JsonProcessingException the json processing exception + */ + @Override + public void validateResponseDto(ResponseDTO responseDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(responseDTO.getHeader()); + g2pcUtilityClass.validateResponse(headerString,CoreConstants.RESPONSE_HEADER); + byte[] json = objectMapper.writeValueAsBytes(responseDTO.getMessage()); + ResponseMessageDTO messageDTO = objectMapper.readValue(json, ResponseMessageDTO.class); + String messageString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(messageDTO); + g2pcUtilityClass.validateResponse(messageString,CoreConstants.SEARCH_RESPONSE); + } + + + /** + * Method to validate signature and encrypted message + * @param metaData metaData + * @param responseDTO responseDTO from which signature is validated. + * @return validated ResponseMessageDTO + * @throws Exception exception might be thrown + */ + @Override + public ResponseMessageDTO signatureValidation(Map metaData, ResponseDTO responseDTO) throws Exception { + + + String p12Password =""; + boolean isEncrypt = false; + boolean isSign=false; + String keyPath=""; + if(metaData.get(CoreConstants.DP_ID).equals(farmerID)){ + p12Password = farmerp12Password; + isEncrypt = isFarmerEncrypt; + isSign = isFarmerSign; + keyPath = farmerKeyPath; + } else if(metaData.get(CoreConstants.DP_ID).equals(mobileID)){ + p12Password = mobilep12Password; + isEncrypt=isMobileEncrypt; + isSign = isMobileSign; + keyPath = mobileKeyPath; + } + log.info("Is encrypted ? -> "+isEncrypt); + log.info("Is signed ? -> "+isSign); + ObjectMapper objectMapper = new ObjectMapper(); + ResponseMessageDTO messageDTO; + + + Boolean isMsgEncrypted = responseDTO.getHeader().getIsMsgEncrypted(); + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(keyPath); + InputStream fis = resource.getInputStream(); + + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String responseHeaderString = objectMapper.writeValueAsString(responseDTO.getHeader()); + String responseSignature = responseDTO.getSignature(); + String messageString = responseDTO.getMessage().toString(); + String data = responseHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(isMsgEncrypted){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(ResponseMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(responseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, ResponseMessageDTO.class); + String responseHeaderString = objectMapper.writeValueAsString(responseDTO.getHeader()); + String responseSignature = responseDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = responseHeaderString+messageString; + log.info("Signature ->"+responseSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = responseDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(),"Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(ResponseMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(responseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, ResponseMessageDTO.class); + } + } + return messageDTO; + } + + @Override + public StatusResponseMessageDTO signatureValidation(Map metaData, StatusResponseDTO statusResponseDTO) throws Exception { + String p12Password =""; + boolean isEncrypt = false; + boolean isSign= false; + String keyPath=""; + if(metaData.get(CoreConstants.DP_ID).equals(farmerID)){ + p12Password = farmerp12Password; + isEncrypt = isFarmerEncrypt; + isSign = isFarmerSign; + keyPath = farmerKeyPath; + } else if(metaData.get(CoreConstants.DP_ID).equals(mobileID)){ + p12Password = mobilep12Password; + isEncrypt=isMobileEncrypt; + isSign = isMobileSign; + keyPath = mobileKeyPath; + } + log.info("Is encrypted ? -> "+isEncrypt); + log.info("Is signed ? -> "+isSign); + ObjectMapper objectMapper = new ObjectMapper(); + StatusResponseMessageDTO messageDTO; + Boolean isMsgEncrypted = statusResponseDTO.getHeader().getIsMsgEncrypted(); + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(keyPath); + InputStream fis = resource.getInputStream(); + + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String responseHeaderString = objectMapper.writeValueAsString(statusResponseDTO.getHeader()); + String responseSignature = statusResponseDTO.getSignature(); + String messageString = statusResponseDTO.getMessage().toString(); + String data = responseHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(isMsgEncrypted){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusResponseMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(statusResponseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusResponseMessageDTO.class); + String responseHeaderString = objectMapper.writeValueAsString(statusResponseDTO.getHeader()); + String responseSignature = statusResponseDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = responseHeaderString+messageString; + log.info("Signature ->"+responseSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature) , fis , p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }catch(IOException e){ + log.info("Rejecting the on-search request in signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = statusResponseDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(),"Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusResponseMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(statusResponseDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusResponseMessageDTO.class); + } + } + return messageDTO; + } + + /** + * + * @param statusResponseDTO statusResponseDTO to validate + * @throws IOException exception might be thrown + * @throws G2pcValidationException exception might be thrown + */ + @Override + public void validateStatusResponseDTO(StatusResponseDTO statusResponseDTO) throws IOException, G2pcValidationException { + ObjectMapper objectMapper = new ObjectMapper(); + byte[] json = objectMapper.writeValueAsBytes(statusResponseDTO.getMessage()); + StatusResponseMessageDTO statusResponseMessageDTO = objectMapper.readValue(json, StatusResponseMessageDTO.class); + String messageString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(statusResponseMessageDTO); + g2pcUtilityClass.validateResponse(messageString,CoreConstants.STATUS_RESPONSE); + } +} + diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/utils/DcCommonUtils.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/utils/DcCommonUtils.java new file mode 100644 index 0000000..cc8d8a7 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/java/g2pc/ref/dc/client/utils/DcCommonUtils.java @@ -0,0 +1,124 @@ +package g2pc.ref.dc.client.utils; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.constants.SftpConstants; +import g2pc.core.lib.dto.sftp.SftpServerConfigDTO; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.security.BearerTokenUtil; +import g2pc.core.lib.security.service.G2pTokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +@Service +public class DcCommonUtils { + + @Autowired + G2pTokenService g2pTokenService; + + @Value("${keycloak.dc.client.realm}") + private String keycloakRealm; + + @Value("${keycloak.dc.client.url}") + private String keycloakURL; + + @Value("${keycloak.dc.master.url}") + private String masterUrl; + + @Value("${keycloak.dc.master.getClientUrl}") + private String getClientUrl; + + @Value("${keycloak.dc.client.clientId}") + private String dcClientId; + + @Value("${keycloak.dc.client.clientSecret}") + private String dcClientSecret; + + @Value("${keycloak.dc.master.clientId}") + private String masterClientId; + + @Value("${keycloak.dc.master.clientSecret}") + private String masterClientSecret; + + @Value("${keycloak.dc.username}") + private String adminUsername; + + @Value("${keycloak.dc.password}") + private String adminPassword; + + @Value("${sftp.listener.host}") + private String sftpDcHost; + + @Value("${sftp.listener.port}") + private int sftpDcPort; + + @Value("${sftp.listener.user}") + private String sftpDcUser; + + @Value("${sftp.listener.password}") + private String sftpDcPassword; + + @Value("${sftp.listener.remote.inbound_directory}") + private String sftpDcRemoteInboundDirectory; + + @Value("${sftp.listener.remote.outbound_directory}") + private String sftpDcRemoteOutboundDirectory; + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpDcLocalInboundDirectory; + + @Value("${sftp.listener.local.outbound_directory}") + private String sftpDcLocalOutboundDirectory; + + public void handleToken() throws G2pHttpException, JsonProcessingException { + String token = BearerTokenUtil.getBearerTokenHeader(); + String introspect = keycloakURL + "/introspect"; + ResponseEntity introspectResponse = g2pTokenService.getInterSpectResponse(introspect, token, dcClientId, dcClientSecret); + if (introspectResponse.getStatusCode().value() == 401) { + throw new G2pHttpException(new G2pcError(introspectResponse.getStatusCode().toString(), introspectResponse.getBody())); + } + if (!g2pTokenService.validateToken(masterUrl, getClientUrl, g2pTokenService.decodeToken(token), + masterClientId, masterClientSecret, adminUsername, adminPassword)) { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_USER_UNAUTHORIZED.toValue(), "User is not authorized")); + } + } + + public SftpServerConfigDTO getSftpConfigForDc() { + SftpServerConfigDTO sftpServerConfigDTO = new SftpServerConfigDTO(); + sftpServerConfigDTO.setHost(sftpDcHost); + sftpServerConfigDTO.setPort(sftpDcPort); + sftpServerConfigDTO.setUser(sftpDcUser); + sftpServerConfigDTO.setPassword(sftpDcPassword); + sftpServerConfigDTO.setAllowUnknownKeys(true); + sftpServerConfigDTO.setStrictHostKeyChecking("no"); + sftpServerConfigDTO.setRemoteInboundDirectory(sftpDcRemoteInboundDirectory); + sftpServerConfigDTO.setRemoteOutboundDirectory(sftpDcRemoteOutboundDirectory); + sftpServerConfigDTO.setLocalInboundDirectory(sftpDcLocalInboundDirectory); + sftpServerConfigDTO.setLocalOutboundDirectory(sftpDcLocalOutboundDirectory); + return sftpServerConfigDTO; + } + + public void deleteFolder(Path path) { + try { + if (Files.isRegularFile(path)) { + Files.delete(path); + return; + } + try (Stream paths = Files.walk(path)) { + paths.filter(p -> p.compareTo(path) != 0).forEach(p -> deleteFolder(p)); // delete all the children folders or files; + Files.delete(path); // delete the folder itself; + } + } catch (IOException ignored) { + ignored.printStackTrace(); + } + } +} diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/1693731657.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/1693731657.p12 new file mode 100644 index 0000000..9a82799 Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/1693731657.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/application-local.yml b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/application-local.yml new file mode 100644 index 0000000..4275b00 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/application-local.yml @@ -0,0 +1,175 @@ +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + + second-datasource: + driverClassName: not_set + url: not_set + username: not_set + password: not_set + + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: 8000 + error: + include-message: always + +spring.data.redis: + repositories.enabled: false + host: 3.109.26.38 + password: cdpi@99221 + port: 6379 + +keycloak: + from_dp: + farmer: + url: not_set + clientId: not_set + clientSecret: not_set + mobile: + url: not_set + clientId: not_set + clientSecret: not_set + dc: + url: not_set + username: not_set + password: not_set + master: + url: not_set + getClientUrl: not_set + clientId: not_set + clientSecret: not_set + client: + url: not_set + realm: not_set + clientId: not_set + clientSecret: not_set + +crypto: + to_dp_farmer: + password: not_set + key_path: not_set + to_dp_mobile: + password: not_set + key_path: not_set + from_dp_farmer: + password: not_set + key_path: not_set + from_dp_mobile: + password: not_set + key_path: not_set + sample: + password: not_set + key.path: not_set + +registry: + api_urls: + farmer_search_api: not_set + mobile_search_api: not_set + farmer_status_api: not_set + mobile_status_api: not_set + +dashboard: + left_panel_url: not_set + right_panel_url: not_set + bottom_panel_url: not_set + post_https_endpoint_url: not_set + clear_dc_db_endpoint_url: not_set + clear_dp1_db_endpoint_url: not_set + clear_dp2_db_endpoint_url: not_set + left_panel_data_endpoint_url: not_set + sftp_post_endpoint_url: not_set + sftp_dc_data_endpoint_url: not_set + sftp_dp1_data_endpoint_url: not_set + sftp_dp2_data_endpoint_url: not_set + dc_status_endpoint_url: not_set + sftp_left_panel_url: not_set + sftp_right_panel_url: not_set + sftp_bottom_panel_url: not_set + https_sunbird_left_panel_url: not_set + https_sunbird_right_panel_url: not_set + https_sunbird_bottom_panel_url: not_set + sftp_sunbird_left_panel_url: not_set + sftp_sunbird_right_panel_url: not_set + sftp_sunbird_bottom_panel_url: not_set + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dp1: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + + dp2: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + elasticsearch: + host: not_set + port: not_set + scheme: not_set \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/application.yml b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/application.yml new file mode 100644 index 0000000..33351c4 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/application.yml @@ -0,0 +1,175 @@ +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + + second-datasource: + driverClassName: not_set + url: not_set + username: not_set + password: not_set + + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: 8000 + error: + include-message: always + +spring.data.redis: + repositories.enabled: false + host: not_set + password: not_set + port: not_set + +keycloak: + from_dp: + farmer: + url: not_set + clientId: not_set + clientSecret: not_set + mobile: + url: not_set + clientId: not_set + clientSecret: not_set + dc: + url: not_set + username: not_set + password: not_set + master: + url: not_set + getClientUrl: not_set + clientId: not_set + clientSecret: not_set + client: + url: not_set + realm: not_set + clientId: not_set + clientSecret: not_set + +crypto: + to_dp_farmer: + password: not_set + key_path: not_set + to_dp_mobile: + password: not_set + key_path: not_set + from_dp_farmer: + password: not_set + key_path: not_set + from_dp_mobile: + password: not_set + key_path: not_set + sample: + password: not_set + key.path: not_set + +registry: + api_urls: + farmer_search_api: not_set + mobile_search_api: not_set + farmer_status_api: not_set + mobile_status_api: not_set + +dashboard: + left_panel_url: not_set + right_panel_url: not_set + bottom_panel_url: not_set + post_https_endpoint_url: not_set + clear_dc_db_endpoint_url: not_set + clear_dp1_db_endpoint_url: not_set + clear_dp2_db_endpoint_url: not_set + left_panel_data_endpoint_url: not_set + sftp_post_endpoint_url: not_set + sftp_dc_data_endpoint_url: not_set + sftp_dp1_data_endpoint_url: not_set + sftp_dp2_data_endpoint_url: not_set + dc_status_endpoint_url: not_set + sftp_left_panel_url: not_set + sftp_right_panel_url: not_set + sftp_bottom_panel_url: not_set + https_sunbird_left_panel_url: not_set + https_sunbird_right_panel_url: not_set + https_sunbird_bottom_panel_url: not_set + sftp_sunbird_left_panel_url: not_set + sftp_sunbird_right_panel_url: not_set + sftp_sunbird_bottom_panel_url: not_set + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dp1: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + + dp2: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + elasticsearch: + host: not_set + port: not_set + scheme: not_set \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/farmer_on_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/farmer_on_search.p12 new file mode 100644 index 0000000..9a83fcb Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/farmer_on_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/farmer_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/farmer_search.p12 new file mode 100644 index 0000000..1873f5d Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/farmer_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/logback-spring.xml b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..ab33f92 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/logback-spring.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/mobile_on_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/mobile_on_search.p12 new file mode 100644 index 0000000..43fe9ea Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/mobile_on_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/mobile_search.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/mobile_search.p12 new file mode 100644 index 0000000..372e8c6 Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/mobile_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/private.p12 b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/private.p12 new file mode 100644 index 0000000..c067bff Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/private.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/schema/RegRecordFarmerSchema.json b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/schema/RegRecordFarmerSchema.json new file mode 100644 index 0000000..58ba7a4 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/schema/RegRecordFarmerSchema.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Message schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "farmer_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "farmer_name": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "season": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "payment_status": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "payment_date": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "payment_amount": { + "type": "number" + } + } , + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/schema/RegRecordMobileSchema.json b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/schema/RegRecordMobileSchema.json new file mode 100644 index 0000000..1201abe --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/resources/schema/RegRecordMobileSchema.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Message schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "farmer_id": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "farmer_name": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "season": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "mobile_number": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "mobile_status": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "created_date": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + } + } , + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardHttps.jsp b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardHttps.jsp new file mode 100644 index 0000000..dc17dff --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardHttps.jsp @@ -0,0 +1,357 @@ +<%@ page contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> + + + + + + + + Dashboard-HTTPS + + + +
+
+
+ + + + + + +
+
+ + + + + + + + + + + +
Data Consumer - Request Tracker +
message_tstransaction_idstatus
+
+
+
+ +
+
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardSftp.jsp b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardSftp.jsp new file mode 100644 index 0000000..1bf39d0 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardSftp.jsp @@ -0,0 +1,223 @@ +<%@ page contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> + + + + + + Dashboard-SFTP + + + +
+
+
+ + + + + + +
+
+ +
+
+
+ +
+
+
+ +
+ + + \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardSftpSse.jsp b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardSftpSse.jsp new file mode 100644 index 0000000..d0e9fda --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/WEB-INF/jsp/dashboardSftpSse.jsp @@ -0,0 +1,485 @@ +<%@ page contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> + + + + + + Dashboard-SFTP + + + +
+
+
+ + + + + + +
+
+ + + + + + + + + + + +
Data Consumer - Inbound +
message_tstransaction_idfile_name
+
+
+
+
+ + + + + + + + + + + + +
Data Producer - Inbound +
dp_namemessage_tstransaction_idfile_name
+
+
+
+
+
+ + + + + + + + + + + +
Data Consumer - Outbound +
message_tstransaction_idfile_name
+
+
+ + + \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/resources/css/styles.css b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/resources/css/styles.css new file mode 100644 index 0000000..e8c34ae --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/main/webapp/resources/css/styles.css @@ -0,0 +1,108 @@ +body { + background-color: rgba(0, 0, 0, 0.1); + color: #fff; + display: flex; + justify-content: space-around; + align-items: center; + height: 100vh; + margin: 0; + padding: 0; + flex-direction: column; +} + +.panel { + width: 95%; + height: 47%; + display: flex; + justify-content: center; + align-items: center; +} + +.topPanel { + background-color: transparent; +} + +.bottomPanel { + background-color: lightyellow; + border: 1px solid #fff; +} + +.subpanel { + width: 50%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + border: 1px solid #fff; +} + +.subpanel1 { + background-color: lightblue; + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.subpanel2 { + background-color: lightsteelblue; + margin-left: 5px; +} + +.contentPanel { + width: 98%; + height: 95%; + display: flex; + justify-content: center; + align-items: center; + margin-top: 5px; + margin-bottom: 5px; +} + +.contentPanel1 { + display: flex; + justify-content: flex-start; + align-items: center; + border: 1px solid #ffffff; + height: auto; +} + +.contentPanel1 button { + /* add some thing later*/ +} + +.contentPanel1 input[type="text"] { + flex-grow: 1; + margin: 0 10px; +} + +button { + background-color: #3498db; + color: white; + border: none; + text-align: center; + text-decoration: none; + display: flex; + font-size: 12px; + margin: 2px 2px; + cursor: pointer; + border-radius: 4px; + width: auto; + padding: 10px; + white-space: nowrap; +} + +input[type="text"] { + padding: 10px; + border: none; + border-radius: 4px; + width: 100%; + box-sizing: border-box; + font-size: 12px; +} + +.contentPanel2 { + display: flex; + justify-content: flex-start; + align-items: center; + border: 1px solid #fff; +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-dc-client/src/test/java/g2pc/ref/dc/client/G2pcRefDcClientApplicationTests.java b/g2pc-reference-apps/g2pc-ref-dc-client/src/test/java/g2pc/ref/dc/client/G2pcRefDcClientApplicationTests.java new file mode 100644 index 0000000..27dc43f --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-dc-client/src/test/java/g2pc/ref/dc/client/G2pcRefDcClientApplicationTests.java @@ -0,0 +1,64 @@ +package g2pc.ref.dc.client; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.config.G2pUnirestHelper; +import g2pc.dc.core.lib.dto.ResponseDataDto; +import g2pc.dc.core.lib.dto.ResponseTrackerDto; +import kong.unirest.HttpResponse; +import kong.unirest.JsonNode; +import kong.unirest.Unirest; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +@Slf4j +class G2pcRefDcClientApplicationTests { + + @Autowired + G2pUnirestHelper g2pUnirestHelper; + + @Test + void contextLoads() { + } + + + @Test + void saveSunbirdResponseEntity() throws JsonProcessingException { + String response_tracker_uri = "http://localhost:8081/api/v1/Response_Tracker"; + ObjectMapper objectMapper = new ObjectMapper(); + ResponseTrackerDto responseTrackerDto = new ResponseTrackerDto(); + responseTrackerDto.setVersion("version"); + responseTrackerDto.setMessageId("messageID"); + responseTrackerDto.setMessageTs("messageTs"); + responseTrackerDto.setAction("action"); + responseTrackerDto.setSenderId("senderId"); + responseTrackerDto.setReceiverId("receiverID"); + responseTrackerDto.setIsMsgEncrypted(true); + responseTrackerDto.setTransactionId("TransactionID"); + responseTrackerDto.setRegistryType("regType"); + responseTrackerDto.setProtocol("protocol"); + responseTrackerDto.setPayloadFilename("payloadFilename"); + responseTrackerDto.setInboundFilename("inboundFilename"); + ResponseDataDto responseDataDto = new ResponseDataDto(); + responseDataDto.setReferenceId("searchRequestDTO.getReferenceId()"); + responseDataDto.setTimestamp("searchRequestDTO.getTimestamp()"); + responseDataDto.setVersion("version"); + responseDataDto.setRegType("regtpe"); + responseDataDto.setRegSubType("regsubtype"); + responseDataDto.setStatus("status"); + responseDataDto.setStatusReasonCode("g2pcerrocode"); + String responseTrackerString = objectMapper.writeValueAsString(responseTrackerDto); + HttpResponse response = Unirest.post(response_tracker_uri) + .header("Content-Type", "application/json") + .body(responseTrackerString) + .asJson();; + + + log.info(response+""); + + + } +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/.gitignore b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/.gitignore new file mode 100644 index 0000000..e23d88f --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +mvnw +mvnw.cmd +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +.idea/ + diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/Dockerfile b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/Dockerfile new file mode 100644 index 0000000..d6f8dd1 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/Dockerfile @@ -0,0 +1,3 @@ +FROM openjdk:17-jdk-alpine +COPY target/*.war dp-farmer-0.1.war +ENTRYPOINT ["java","-jar","/dp-farmer-0.1.war"] \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/README.md b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/README.md new file mode 100644 index 0000000..e69de29 diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/pom.xml b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/pom.xml new file mode 100644 index 0000000..5ec40ae --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.0.12 + + + war + g2pc.ref.farmer.regsvc + g2pc-ref-farmer-regsvc + 0.0.1-SNAPSHOT + g2pc-ref-farmer-regsvc + g2pc-ref-farmer-regsvc + + 17 + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + g2pc.dp.core.lib + g2pc-dp-core-library + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.postgresql + postgresql + 42.5.4 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springdoc + springdoc-openapi-ui + 1.6.15 + + + org.springframework.security + spring-security-web + 6.1.2 + + + com.auth0 + java-jwt + 4.4.0 + + + jakarta.validation + jakarta.validation-api + 3.0.2 + + + org.springframework.security + spring-security-config + 6.1.2 + + + org.springframework.boot + spring-boot-starter-validation + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + javax.servlet + jstl + 1.2 + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/scheduler-sequence-diagram.puml b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/scheduler-sequence-diagram.puml new file mode 100644 index 0000000..0912e75 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/scheduler-sequence-diagram.puml @@ -0,0 +1,43 @@ +@startuml +'https://plantuml.com/sequence-diagram + +actor Schedular +participant Scheduler +participant FarmerResponseBuilderService +participant TxnTrackerRedisService +participant TxnTrackerDbService +participant MsgTrackerEntity +participant ResponseBuilderService + + +Schedular -> Scheduler +Scheduler -> TxnTrackerRedisService : getCacheKeys(Constants.CACHE_KEY_SEARCH_STRING); +TxnTrackerRedisService -> Scheduler : return cacheKeysList; +Scheduler ->Scheduler : forEach(cacheKeysList) loop +Scheduler -> TxnTrackerRedisService : getRequestData(cacheKey); +TxnTrackerRedisService -> Scheduler : return requestData; +Scheduler ->Scheduler : if( requestData.getStatus == PDNG) +Scheduler -> TxnTrackerDbService : txnTrackerDbService.saveRequestDetails(requestDTO); +TxnTrackerDbService -> Scheduler : return msgTrackerEntity; +Scheduler -> MsgTrackerEntity : getTxnTrackerEntityList() +MsgTrackerEntity ->Scheduler : return queryDTOList +Scheduler -> FarmerResponseBuilderService : getRegFarmerRecords(queryDTOList) +FarmerResponseBuilderService ->Scheduler : return refRecordsStringsList +Scheduler -> TxnTrackerDbService : getUpdatedSearchResponseList(requestDTO, refRecordsStringsList); +TxnTrackerDbService -> Scheduler : return searchResponseDTOList +Scheduler -> ResponseBuilderService : getResponseHeaderDTO(msgTrackerEntity) +ResponseBuilderService -> Scheduler : return headerDTO; +Scheduler -> ResponseBuilderService : buildResponseMessage(transactionId, searchResponseDTOList); +ResponseBuilderService -> Scheduler : return messageDTO; +Scheduler -> ResponseBuilderService : buildResponseString("signature",headerDTO, responseMessageDTO); +ResponseBuilderService -> Scheduler : return responseString; +Scheduler -> ResponseBuilderService : sendOnSearchResponse(responseString, onSearchURL,dcClientId,dcClientSecret ,keyClockClientTokenUrl); +ResponseBuilderService -> Scheduler : return G2pcError; +Scheduler -> Scheduler : if(G2pcError!=200) +Scheduler->Scheduler : throw new G2pHttpException(g2pcError); +Scheduler -> Scheduler : else +Scheduler -> TxnTrackerRedisService : updateRequestDetails(cacheKey, HeaderStatusENUM.SUCC.toValue(), cacheDTO); + + + +@enduml \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/search-seq-diagram.puml b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/search-seq-diagram.puml new file mode 100644 index 0000000..d66e541 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/search-seq-diagram.puml @@ -0,0 +1,50 @@ +@startuml +'https://plantuml.com/sequence-diagram + +autonumber + +Actor actor +participant RegistryController +participant BearerTokenUtil +participant FarmerValidationService +participant G2pTokenService +participant AsymmetricSignatureService +participant G2pEncryptDecrypt +participant RequestHandlerService + +actor -> RegistryController : responseString +RegistryController -> BearerTokenUtil : getBearerTokenHeader() +BearerTokenUtil -> RegistryController : return token; +RegistryController ->G2pTokenService : getInterSpectResponse(introspect, token, adminClientId, adminClientSecret); +RegistryController <- G2pTokenService : return introspectResponse; +RegistryController ->RegistryController : if(introspectResponse.getStatusCode)!=200 +RegistryController -> actor : Unauthorized user +RegistryController -> RegistryController : else +RegistryController -> G2pTokenService : if(!validateToken(masterAdminUrl, getClientUrl, g2pTokenService.decodeToken(token))) +RegistryController -> actor : Unauthorized user +RegistryController ->RegistryController : else +RegistryController -> FarmerValidationService : signatureValidation(metaData, responseDTO); +FarmerValidationService -> AsymmetricSignatureService : verifySignature(data.getBytes(), Base64.getDecoder().decode(responseSignature)) +FarmerValidationService -> G2pEncryptDecrypt : g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); +FarmerValidationService ->FarmerValidationService : if signature or encryption not validateToken +FarmerValidationService -> RegistryController : err.invalid.signature or err.encryption.invalid +RegistryController -> actor : Unauthorized user +FarmerValidationService -> FarmerValidationService : else +FarmerValidationService -> RegistryController : return messageDto +RegistryController -> FarmerValidationService : validateRequestDTO(responseDTO) +FarmerValidationService -> RegistryController : if(!alidateResponseHeader(responseHeader)) +RegistryController -> actor : err.request.bad +FarmerValidationService ->FarmerValidationService : else if(!validateRegRecords(messageDTO)) +FarmerValidationService ->actor : err.request.bad +FarmerValidationService -> FarmerValidationService : else if(!) +FarmerValidationService -> RequestHandlerService : validateResponseMessage(messageDTO) +FarmerValidationService ->actor : err.request.bad +FarmerValidationService -> FarmerValidationService : else +FarmerValidationService -> RegistryController +RegistryController -> RequestHandlerService : getResponse(responseDTO); +RequestHandlerService ->RegistryController :acknowledgementDTO +RegistryController ->actor : acknowledgementDTO + + + +@enduml \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/G2pcRefFarmerRegsvcApplication.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/G2pcRefFarmerRegsvcApplication.java new file mode 100644 index 0000000..f9383ae --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/G2pcRefFarmerRegsvcApplication.java @@ -0,0 +1,23 @@ +package g2pc.ref.farmer.regsvc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +@SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) +@ComponentScan({"g2pc.ref.farmer.regsvc","g2pc.core.lib","g2pc.dp.core.lib","g2pc.ref.farmer.regsvc", + "g2pc.ref.farmer.regsvc.auth.service","g2pc.ref.farmer.regsvc.auth.serviceImpl", + "org.springframework.security.config.annotation.web.builders.HttpSecurity", + "g2pc.dp.core.lib.service","g2pc.ref.farmer.regsvc.auth.controller"}) +@EnableScheduling +@EnableWebSecurity +public class G2pcRefFarmerRegsvcApplication { + + public static void main(String[] args) { + SpringApplication.run(G2pcRefFarmerRegsvcApplication.class, args); + } + +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/CorsConfig.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/CorsConfig.java new file mode 100644 index 0000000..a4e9e3c --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/CorsConfig.java @@ -0,0 +1,22 @@ +package g2pc.ref.farmer.regsvc.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Value("${dashboard.cors_origin_url}") + private String corsOriginUrl; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins(corsOriginUrl) + .allowedMethods("*") + .allowedHeaders("*"); + } +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/ObjectMapperConfig.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/ObjectMapperConfig.java new file mode 100644 index 0000000..ef07586 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/ObjectMapperConfig.java @@ -0,0 +1,20 @@ +package g2pc.ref.farmer.regsvc.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ObjectMapperConfig { + + @Bean + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + return objectMapper; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/SecurityConfig.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/SecurityConfig.java new file mode 100644 index 0000000..8229381 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/config/SecurityConfig.java @@ -0,0 +1,33 @@ +package g2pc.ref.farmer.regsvc.config; + +import g2pc.core.lib.security.G2pTokenVerification; +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.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +@Configuration +public class SecurityConfig { + @Bean + public G2pTokenVerification JWTTokenFilter() { + return new G2pTokenVerification(); + } + + @Bean + public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeRequests(authorizeRequests -> + authorizeRequests + .anyRequest().permitAll() + .and() + .addFilterBefore(JWTTokenFilter(), BasicAuthenticationFilter.class) + ) + .sessionManagement(sessionManagement -> + sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .csrf().disable(); + + return http.build(); + } +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/constants/Constants.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/constants/Constants.java new file mode 100644 index 0000000..2458276 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/constants/Constants.java @@ -0,0 +1,25 @@ +package g2pc.ref.farmer.regsvc.constants; + +public class Constants { + + private Constants() { + } + + public static final String SEARCH_REQUEST_RECEIVED = "Search request received successfully"; + + public static final String INVALID_RESPONSE = "Invalid Response received from server"; + + public static final String CONFLICT = "CONFLICT"; + + public static final String INVALID_AUTHORIZATION = "Invalid Authorization"; + + public static final String CACHE_KEY_SEARCH_STRING = "request-farmer*"; + + public static final String CACHE_KEY_STRING = "request-farmer-"; + + public static final String CONFIGURATION_MISMATCH_ERROR = "Configurations are not matching "; + + public static final String STATUS_CACHE_KEY_STRING = "status-request-farmer-"; + + public static final String STATUS_CACHE_KEY_SEARCH_STRING = "status-request-farmer*"; +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/rest/DpDashboardController.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/rest/DpDashboardController.java new file mode 100644 index 0000000..f9eb627 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/rest/DpDashboardController.java @@ -0,0 +1,21 @@ +package g2pc.ref.farmer.regsvc.controller.rest; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class DpDashboardController { + + @Value("${dashboard.dp_dashboard_url}") + private String dpDashboardUrl; + + @GetMapping("/dashboard") + public String showDashboardPage(Model model) { + model.addAttribute("dp_dashboard_url", dpDashboardUrl); + return "dashboard"; + } + + +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/rest/RegistryController.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/rest/RegistryController.java new file mode 100644 index 0000000..3aff875 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/rest/RegistryController.java @@ -0,0 +1,230 @@ +package g2pc.ref.farmer.regsvc.controller.rest; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.exceptionhandler.ErrorResponse; +import g2pc.core.lib.exceptionhandler.ValidationErrorResponse; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.core.lib.security.service.AsymmetricSignatureService; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import g2pc.core.lib.security.service.G2pTokenService; +import g2pc.dp.core.lib.repository.MsgTrackerRepository; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.dp.core.lib.utils.DpCommonUtils; +import g2pc.ref.farmer.regsvc.constants.Constants; +import g2pc.ref.farmer.regsvc.service.DpSftpPushUpdateService; +import g2pc.ref.farmer.regsvc.service.FarmerValidationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +/** + * The type Registry controller. + */ +@RestController +@Slf4j +@RequestMapping(produces = "application/json") +@Tag(name = "Provider", description = "Provider APIs") +public class RegistryController { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + FarmerValidationService farmerValidationService; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Autowired + G2pTokenService g2pTokenService; + + @Autowired + private AsymmetricSignatureService asymmetricSignatureService; + + @Autowired + private DpCommonUtils dpCommonUtils; + + @Autowired + private MsgTrackerRepository msgTrackerRepository; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private DpSftpPushUpdateService dpSftpPushUpdateService; + + @Value("${dashboard.cors_origin_url}") + private String corsOriginUrl; + + /** + * Get search request from DC + * + * @param requestString required + * @return Search request received acknowledgement + * @throws JsonProcessingException the json processing exception + * @throws ResponseStatusException the response status exception + * @throws G2pcValidationException the validation exception + */ + @SuppressWarnings("unchecked") + @Operation(summary = "Receive search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/registry/search") + public AcknowledgementDTO handleRequest(@RequestBody String requestString) throws Exception { + dpCommonUtils.handleToken(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); + RequestMessageDTO messageDTO = null; + + Map metaData = (Map) requestDTO.getHeader().getMeta().getData(); + + messageDTO = farmerValidationService.signatureValidation(metaData, requestDTO); + requestDTO.setMessage(messageDTO); + String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); + try { + farmerValidationService.validateRequestDTO(requestDTO); + return requestHandlerService.buildCacheRequest( + objectMapper.writeValueAsString(requestDTO), cacheKey, + CoreConstants.SEND_PROTOCOL_HTTPS, sunbirdEnabled); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } + + /** + * TokenIsNotValidException + * Handle validation exception error response. + * + * @param ex the ValidationException + * @return the error response + */ + @ExceptionHandler(value + = G2pcValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ValidationErrorResponse + handleValidationException( + G2pcValidationException ex) { + return new ValidationErrorResponse( + ex.getG2PcErrorList()); + } + + /** + * Handle validation exception error response. + * + * @param ex the ValidationException + * @return the error response + */ + @ExceptionHandler(value = G2pHttpException.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public ErrorResponse handleG2pHttpStatusException(G2pHttpException ex) { + return new ErrorResponse(ex.getG2PcError()); + } + + /** + * Clear message tracker DB + * Clear Redis cache + */ + @GetMapping("/private/api/v1/registry/clear-db") + public void clearDb() throws G2pHttpException, IOException { + dpCommonUtils.handleToken(); + msgTrackerRepository.deleteAll(); + log.info("DP-1 DB cleared"); + Set keys = redisTemplate.keys("*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + log.info("DP-1 Redis cache cleared"); + } + + @PostMapping("/public/api/v1/registry/search") + public AcknowledgementDTO demoSearch(@RequestBody RequestDTO requestDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + RequestMessageDTO messageDTO = objectMapper.convertValue(requestDTO.getMessage(), RequestMessageDTO.class); + + String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); + + return requestHandlerService.buildCacheRequest(objectMapper.writeValueAsString(requestDTO), cacheKey, + CoreConstants.SEND_PROTOCOL_HTTPS,sunbirdEnabled); + } + + @Operation(summary = "Receive search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @PostMapping("/private/api/v1/registry/txn/status") + public AcknowledgementDTO handleStatusRequest(@RequestBody String requestString) throws Exception { + dpCommonUtils.handleToken(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + StatusRequestDTO statusRequestDTO = objectMapper.readerFor(StatusRequestDTO.class). + readValue(requestString); + StatusRequestMessageDTO statusRequestMessageDTO = null; + + Map metaData = (Map) statusRequestDTO.getHeader().getMeta().getData(); + + statusRequestMessageDTO = farmerValidationService.signatureValidation(metaData, statusRequestDTO); + statusRequestDTO.setMessage(statusRequestMessageDTO); + String cacheKey = Constants.STATUS_CACHE_KEY_STRING + statusRequestMessageDTO.getTransactionId(); + try { + farmerValidationService.validateStatusRequestDTO(statusRequestDTO); + return requestHandlerService.buildCacheStatusRequest( + objectMapper.writeValueAsString(statusRequestDTO), cacheKey, CoreConstants.SEND_PROTOCOL_HTTPS); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } + + @GetMapping(value = "/dashboard/sftp/dp1/data", produces = "text/event-stream") + public SseEmitter sseEmitterFirstPanel() { + return dpSftpPushUpdateService.register(); + } +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/sftp/DcSftpListener.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/sftp/DcSftpListener.java new file mode 100644 index 0000000..37e82b6 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/controller/sftp/DcSftpListener.java @@ -0,0 +1,99 @@ +package g2pc.ref.farmer.regsvc.controller.sftp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.ref.farmer.regsvc.constants.Constants; +import g2pc.ref.farmer.regsvc.dto.SftpDpData; +import g2pc.ref.farmer.regsvc.service.DpSftpPushUpdateService; +import g2pc.ref.farmer.regsvc.service.FarmerValidationService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.web.server.ResponseStatusException; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +@Configuration +@Slf4j +public class DcSftpListener { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpLocalDirectoryInbound; + + @Value("${sftp.listener.local.outbound_directory}") + private String sftpLocalDirectoryOutbound; + + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + FarmerValidationService farmerValidationService; + + @Autowired + private DpSftpPushUpdateService dpSftpPushUpdateService; + + @SuppressWarnings("unchecked") + @ServiceActivator(inputChannel = "sftpInbound") + public void handleMessageInbound(Message message) { + try { + File file = message.getPayload(); + log.info("Received Message from inbound directory of dp-1: {}", file.getName()); + if (ObjectUtils.isNotEmpty(file) && file.getName().contains(".json")) { + String requestString = new String(Files.readAllBytes(file.toPath())); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); + RequestMessageDTO messageDTO; + + Map metaData = (Map) requestDTO.getHeader().getMeta().getData(); + + messageDTO = farmerValidationService.signatureValidation(metaData, requestDTO); + requestDTO.setMessage(messageDTO); + String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); + try { + farmerValidationService.validateRequestDTO(requestDTO); + requestHandlerService.buildCacheRequest( + objectMapper.writeValueAsString(requestDTO), cacheKey, + CoreConstants.SEND_PROTOCOL_SFTP, sunbirdEnabled); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } + Files.deleteIfExists(Path.of(sftpLocalDirectoryInbound + "/" + file.getName())); + } catch (Exception e) { + log.error("Error: ", e); + } + } + + @ServiceActivator(inputChannel = "errorChannel") + public void handleError(Message message) { + Throwable error = (Throwable) message.getPayload(); + log.error("Handling ERROR: {}", error.getMessage()); + } +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/SftpDpData.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/SftpDpData.java new file mode 100644 index 0000000..7800054 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/SftpDpData.java @@ -0,0 +1,19 @@ +package g2pc.ref.farmer.regsvc.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SftpDpData { + + private String dpType; + + private String messageTs; + + private String transactionId; + + private String fileName; +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/request/QueryFarmerDTO.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/request/QueryFarmerDTO.java new file mode 100644 index 0000000..c5d6faf --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/request/QueryFarmerDTO.java @@ -0,0 +1,16 @@ +package g2pc.ref.farmer.regsvc.dto.request; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class QueryFarmerDTO { + + @JsonProperty("query_params") + private QueryParamsFarmerDTO queryParams; +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/request/QueryParamsFarmerDTO.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/request/QueryParamsFarmerDTO.java new file mode 100644 index 0000000..1fb4526 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/request/QueryParamsFarmerDTO.java @@ -0,0 +1,19 @@ +package g2pc.ref.farmer.regsvc.dto.request; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class QueryParamsFarmerDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("season") + private String season; +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/response/DataFarmerDTO.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/response/DataFarmerDTO.java new file mode 100644 index 0000000..5300299 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/response/DataFarmerDTO.java @@ -0,0 +1,18 @@ +package g2pc.ref.farmer.regsvc.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +public class DataFarmerDTO { + + @JsonProperty("reg_records") + private List regRecords; +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/response/RegRecordFarmerDTO.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/response/RegRecordFarmerDTO.java new file mode 100644 index 0000000..3deb88a --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/dto/response/RegRecordFarmerDTO.java @@ -0,0 +1,31 @@ +package g2pc.ref.farmer.regsvc.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class RegRecordFarmerDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("farmer_name") + private String farmerName; + + @JsonProperty("season") + private String season; + + @JsonProperty("payment_status") + private String paymentStatus; + + @JsonProperty("payment_date") + private String paymentDate; + + @JsonProperty("payment_amount") + private Double paymentAmount; +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/entity/FarmerInfoEntity.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/entity/FarmerInfoEntity.java new file mode 100644 index 0000000..2bba62d --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/entity/FarmerInfoEntity.java @@ -0,0 +1,32 @@ +package g2pc.ref.farmer.regsvc.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "farmer_info") +public class FarmerInfoEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + private String farmerId; + + private String farmerName; + + private String season; + + private String paymentStatus; + + private String paymentDate; + + private Double paymentAmount; +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/repository/FarmerInfoRepository.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/repository/FarmerInfoRepository.java new file mode 100644 index 0000000..9eb7ed1 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/repository/FarmerInfoRepository.java @@ -0,0 +1,11 @@ +package g2pc.ref.farmer.regsvc.repository; + +import g2pc.ref.farmer.regsvc.entity.FarmerInfoEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.Optional; + +@Repository +public interface FarmerInfoRepository extends JpaRepository { + Optional findBySeasonAndFarmerId(String season, String farmerId); +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/scheduler/Scheduler.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/scheduler/Scheduler.java new file mode 100644 index 0000000..ea4f75b --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/scheduler/Scheduler.java @@ -0,0 +1,111 @@ +package g2pc.ref.farmer.regsvc.scheduler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import g2pc.dp.core.lib.service.ResponseBuilderService; +import g2pc.dp.core.lib.service.TxnTrackerDbService; +import g2pc.dp.core.lib.service.TxnTrackerRedisService; +import g2pc.ref.farmer.regsvc.constants.Constants; +import g2pc.ref.farmer.regsvc.service.FarmerResponseBuilderService; +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +public class Scheduler { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Autowired + private ResponseBuilderService responseBuilderService; + + @Autowired + private FarmerResponseBuilderService farmerResponseBuilderService; + + @Autowired + private TxnTrackerRedisService txnTrackerRedisService; + + @Autowired + private TxnTrackerDbService txnTrackerDbService; + + /** + * This method is a scheduled task that runs every minute. + * It retrieves data from a Redis cache, processes it, and sends a response. + * If the status of the data is 'PDNG', it processes the data and updates the status to 'SUCC'. + * If an exception occurs during the process, it logs the exception message. + */ + @SuppressWarnings("unchecked") + @Scheduled(cron = "0 */1 * ? * *")// runs every 1 min. + @Transactional + public void responseScheduler() { + try { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + + List cacheKeysList = txnTrackerRedisService.getCacheKeys(Constants.CACHE_KEY_SEARCH_STRING); + for (String cacheKey : cacheKeysList) { + String requestData = txnTrackerRedisService.getRequestData(cacheKey); + CacheDTO cacheDTO = objectMapper.readerFor(CacheDTO.class).readValue(requestData); + if (cacheDTO.getStatus().equals(HeaderStatusENUM.PDNG.toValue())) { + String protocol = cacheDTO.getProtocol(); + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class).readValue(cacheDTO.getData()); + MsgTrackerEntity msgTrackerEntity = txnTrackerDbService.saveRequestDetails(requestDTO, protocol, sunbirdEnabled); + List queryDTOList = msgTrackerEntity.getTxnTrackerEntityList().stream() + .map(txnTrackerEntity -> { + try { + return objectMapper.readValue(txnTrackerEntity.getQuery(), QueryDTO.class); + } catch (JsonProcessingException e) { + return null; + } + }).toList(); + List refRecordsStringsList = farmerResponseBuilderService.getRegFarmerRecords(queryDTOList); + G2pcError g2pcError = responseBuilderService.buildOnSearchScheduler(refRecordsStringsList, cacheDTO, sunbirdEnabled); + log.info("on-search response - " + g2pcError.getCode()); + if (!g2pcError.getCode().equals(HttpStatus.OK.toString())) { + throw new G2pHttpException(g2pcError); + } else { + txnTrackerRedisService.updateRequestDetails(cacheKey, HeaderStatusENUM.SUCC.toValue(), cacheDTO); + } + } + } + List statusCacheKeysList = txnTrackerRedisService.getCacheKeys(Constants.STATUS_CACHE_KEY_SEARCH_STRING); + for (String cacheKey : statusCacheKeysList) { + String requestData = txnTrackerRedisService.getRequestData(cacheKey); + CacheDTO cacheDTO = objectMapper.readerFor(CacheDTO.class).readValue(requestData); + if (cacheDTO.getStatus().equals(HeaderStatusENUM.PDNG.toValue())) { + StatusRequestDTO statusRequestDTO = objectMapper.readerFor(StatusRequestDTO.class).readValue(cacheDTO.getData()); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.convertValue(statusRequestDTO.getMessage(), StatusRequestMessageDTO.class); + G2pcError g2pcError = responseBuilderService.buildOnStatusScheduler(cacheDTO); + log.info("on-status response - " + g2pcError.getCode()); + if (!g2pcError.getCode().equals(HttpStatus.OK.toString())) { + throw new G2pHttpException(g2pcError); + } else { + txnTrackerDbService.updateMessageTrackerStatusDb(statusRequestMessageDTO.getTransactionId()); + txnTrackerRedisService.updateRequestDetails(cacheKey, HeaderStatusENUM.SUCC.toValue(), cacheDTO); + } + } + } + } catch (Exception ex) { + log.error("Exception in responseScheduler: {}", ex.getMessage()); + } + } +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/DpSftpPushUpdateService.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/DpSftpPushUpdateService.java new file mode 100644 index 0000000..19e81db --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/DpSftpPushUpdateService.java @@ -0,0 +1,10 @@ +package g2pc.ref.farmer.regsvc.service; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +public interface DpSftpPushUpdateService { + + SseEmitter register(); + + void pushUpdate(Object update); +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/FarmerResponseBuilderService.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/FarmerResponseBuilderService.java new file mode 100644 index 0000000..f9f58ae --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/FarmerResponseBuilderService.java @@ -0,0 +1,23 @@ +package g2pc.ref.farmer.regsvc.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.ref.farmer.regsvc.dto.response.RegRecordFarmerDTO; +import g2pc.ref.farmer.regsvc.entity.FarmerInfoEntity; +import org.elasticsearch.action.search.SearchResponse; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public interface FarmerResponseBuilderService { + + RegRecordFarmerDTO getRegRecordFarmerDTOFromDb(FarmerInfoEntity farmerInfoEntity); + + RegRecordFarmerDTO getRegRecordFarmerDTOFromSunbird(SearchResponse searchResponse) throws JsonProcessingException; + + List getRegFarmerRecords(List queryDTOList) throws IOException; + + + +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/FarmerValidationService.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/FarmerValidationService.java new file mode 100644 index 0000000..c55090c --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/service/FarmerValidationService.java @@ -0,0 +1,24 @@ +package g2pc.ref.farmer.regsvc.service; + +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.exceptions.G2pcValidationException; + +import java.io.IOException; +import java.util.Map; +/** + * The interface Farmer validation service. + */ +public interface FarmerValidationService { + + void validateRequestDTO (RequestDTO requestDTO) throws Exception; + + RequestMessageDTO signatureValidation(Map metaData, RequestDTO requestDTO ) throws Exception; + + StatusRequestMessageDTO signatureValidation(Map metaData, StatusRequestDTO requestDTO) throws Exception ; + + void validateStatusRequestDTO (StatusRequestDTO requestDTO) throws IOException, G2pcValidationException; + + } diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/DpSftpPushUpdateServiceImpl.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/DpSftpPushUpdateServiceImpl.java new file mode 100644 index 0000000..0e475da --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/DpSftpPushUpdateServiceImpl.java @@ -0,0 +1,50 @@ +package g2pc.ref.farmer.regsvc.serviceimpl; + +import g2pc.ref.farmer.regsvc.dto.SftpDpData; +import g2pc.ref.farmer.regsvc.service.DpSftpPushUpdateService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class DpSftpPushUpdateServiceImpl implements DpSftpPushUpdateService { + + private final List emitters = new ArrayList<>(); + + public SseEmitter register() { + int minutes = 15; + long timeout = (long) minutes * 60000; + SseEmitter emitter = new SseEmitter(timeout); + this.emitters.add(emitter); + emitter.onCompletion(() -> this.emitters.remove(emitter)); + emitter.onTimeout(() -> this.emitters.remove(emitter)); + log.info("SSE emitter registered" + emitter); + return emitter; + } + + public void pushUpdate(Object update) { + List deadEmitters = new ArrayList<>(); + this.emitters.forEach(emitter -> { + try { + emitter.send(update); + } catch (IOException e) { + deadEmitters.add(emitter); + } + }); + this.emitters.removeAll(deadEmitters); + } + + public SftpDpData getSftpDpData(String dpType, String messageTs, String transactionId, String filename){ + SftpDpData sftpDpData = new SftpDpData(); + sftpDpData.setDpType(dpType); + sftpDpData.setMessageTs(messageTs); + sftpDpData.setTransactionId(transactionId); + sftpDpData.setFileName(filename); + return sftpDpData; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/FarmerResponseBuilderServiceImpl.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/FarmerResponseBuilderServiceImpl.java new file mode 100644 index 0000000..093f891 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/FarmerResponseBuilderServiceImpl.java @@ -0,0 +1,106 @@ +package g2pc.ref.farmer.regsvc.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.core.lib.service.ElasticsearchService; +import g2pc.ref.farmer.regsvc.dto.request.QueryParamsFarmerDTO; +import g2pc.ref.farmer.regsvc.dto.response.RegRecordFarmerDTO; +import g2pc.ref.farmer.regsvc.entity.FarmerInfoEntity; +import g2pc.ref.farmer.regsvc.repository.FarmerInfoRepository; +import g2pc.ref.farmer.regsvc.service.FarmerResponseBuilderService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.elasticsearch.action.search.SearchResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.*; + +@Service +@Slf4j +public class FarmerResponseBuilderServiceImpl implements FarmerResponseBuilderService { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Autowired + private FarmerInfoRepository farmerInfoRepository; + + @Autowired + private ElasticsearchService elasticsearchService; + + /** + * Get farmer records information from DB + * + * @param farmerInfoEntity required + * @return Farmer records + */ + @Override + public RegRecordFarmerDTO getRegRecordFarmerDTOFromDb(FarmerInfoEntity farmerInfoEntity) { + RegRecordFarmerDTO dto = new RegRecordFarmerDTO(); + dto.setFarmerId(farmerInfoEntity.getFarmerId()); + dto.setFarmerName(farmerInfoEntity.getFarmerName()); + dto.setSeason(farmerInfoEntity.getSeason()); + dto.setPaymentStatus(farmerInfoEntity.getPaymentStatus()); + dto.setPaymentDate(farmerInfoEntity.getPaymentDate()); + dto.setPaymentAmount(farmerInfoEntity.getPaymentAmount()); + return dto; + } + + /** + * Get farmer records information from Sunbird + * + * @param searchResponse required + * @return Farmer records + */ + @Override + public RegRecordFarmerDTO getRegRecordFarmerDTOFromSunbird(SearchResponse searchResponse) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + String sourceAsString = searchResponse.getHits().getHits()[0].getSourceAsString(); + return objectMapper.readValue(sourceAsString, RegRecordFarmerDTO.class); + } + + /** + * Get farmer records information string + * + * @param queryDTOList required + * @return List of farmer records + */ + @Override + public List getRegFarmerRecords(List queryDTOList) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + List regFarmerRecordsList = new ArrayList<>(); + for (QueryDTO queryDTO : queryDTOList) { + String queryParams = objectMapper.writeValueAsString(queryDTO.getQueryParams()); + QueryParamsFarmerDTO queryParamsFarmerDTO = objectMapper.readValue(queryParams, QueryParamsFarmerDTO.class); + String farmerId = queryParamsFarmerDTO.getFarmerId(); + String season = queryParamsFarmerDTO.getSeason(); + if (Boolean.TRUE.equals(sunbirdEnabled)) { + log.info("Record fetched from sunbird"); + Map fieldValues = new HashMap<>(); + fieldValues.put("farmer_id.keyword", farmerId); + fieldValues.put("season.keyword", season); + SearchResponse response = elasticsearchService.exactSearch("farmer_info", fieldValues); + if (response.getHits().getHits().length > 0) { + RegRecordFarmerDTO regRecordFarmerDTO = getRegRecordFarmerDTOFromSunbird(response); + regFarmerRecordsList.add(objectMapper.writeValueAsString(regRecordFarmerDTO)); + } else { + regFarmerRecordsList.add(StringUtils.EMPTY); + } + } else { + log.info("Record fetched from postgres"); + Optional optional = farmerInfoRepository.findBySeasonAndFarmerId(season, farmerId); + if (optional.isPresent()) { + RegRecordFarmerDTO regRecordFarmerDTO = getRegRecordFarmerDTOFromDb(optional.get()); + regFarmerRecordsList.add(objectMapper.writeValueAsString(regRecordFarmerDTO)); + } else { + regFarmerRecordsList.add(StringUtils.EMPTY); + } + } + } + return regFarmerRecordsList; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/FarmerValidationServiceImpl.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/FarmerValidationServiceImpl.java new file mode 100644 index 0000000..c26d7f3 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/java/g2pc/ref/farmer/regsvc/serviceimpl/FarmerValidationServiceImpl.java @@ -0,0 +1,330 @@ +package g2pc.ref.farmer.regsvc.serviceimpl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.JsonSchema; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.constants.G2pSecurityConstants; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.core.lib.security.service.AsymmetricSignatureService; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import g2pc.core.lib.security.service.G2pcUtilityClass; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.ref.farmer.regsvc.constants.Constants; +import g2pc.ref.farmer.regsvc.dto.request.QueryFarmerDTO; +import g2pc.ref.farmer.regsvc.dto.request.QueryParamsFarmerDTO; +import g2pc.ref.farmer.regsvc.service.FarmerValidationService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.SignatureException; +import java.util.*; + + +/** + * The type Farmer validation service. + */ +@Service +@Slf4j +public class FarmerValidationServiceImpl implements FarmerValidationService { + + /** + * The Request handler service. + */ + @Autowired + RequestHandlerService requestHandlerService; + + @Value("${crypto.from_dc.support_encryption}") + private boolean isEncrypt; + + @Value("${crypto.from_dc.support_signature}") + private boolean isSign; + + @Value("${crypto.from_dc.password}") + private String p12Password; + + @Autowired + private AsymmetricSignatureService asymmetricSignatureService; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Autowired + private ResourceLoader resourceLoader; + + @Value("${crypto.from_dc.key_path}") + private String farmer_key_path; + + @Autowired + G2pcUtilityClass g2pcUtilityClass; + + /** + * Validate request dto. + * + * @param requestDTO the request dto + * @throws G2pcValidationException the validation exception + * @throws JsonProcessingException the json processing exception + */ + @Override + public void validateRequestDTO(RequestDTO requestDTO) throws G2pcValidationException, IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(QueryDTO.class, + QueryFarmerDTO.class, QueryParamsFarmerDTO.class); + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + RequestMessageDTO messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(requestDTO.getHeader()); + g2pcUtilityClass.validateResponse(headerString , CoreConstants.REQUEST_HEADER); + String messageString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(messageDTO); + g2pcUtilityClass.validateResponse(messageString,CoreConstants.SEARCH_REQUEST); + } + + + + /** + * Method to validate signature and encrypted message + * @param metaData + * @param requestDTO + * @return + * @throws Exception + */ + @Override + public RequestMessageDTO signatureValidation(Map metaData, RequestDTO requestDTO) throws Exception { + + ObjectMapper objectMapper = new ObjectMapper(); + RequestMessageDTO messageDTO; + Boolean isMsgEncrypted = requestDTO.getHeader().getIsMsgEncrypted(); + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(farmer_key_path); + InputStream fis = resource.getInputStream(); + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = requestDTO.getMessage().toString(); + String data = requestHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ->"+e.getMessage())); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(isMsgEncrypted){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException | InvalidAlgorithmParameterException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(RequestMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = requestHeaderString+messageString; + log.info("Signature ->"+requestSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = requestDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException | InvalidAlgorithmParameterException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(RequestMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + } + } + requestDTO.setMessage(messageDTO); + return messageDTO; + } + + @Override + public StatusRequestMessageDTO signatureValidation(Map metaData, StatusRequestDTO requestDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + StatusRequestMessageDTO messageDTO; + Boolean isMsgEncrypted = requestDTO.getHeader().getIsMsgEncrypted(); + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(farmer_key_path); + InputStream fis = resource.getInputStream(); + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = requestDTO.getMessage().toString(); + String data = requestHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ->"+e.getMessage())); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(isMsgEncrypted){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusRequestMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = requestHeaderString+messageString; + log.info("Signature ->"+requestSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = requestDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException e ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusRequestMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + } + } + requestDTO.setMessage(messageDTO); + return messageDTO; + } + + @Override + public void validateStatusRequestDTO(StatusRequestDTO statusRequestDTO) throws IOException, G2pcValidationException { + ObjectMapper objectMapper = new ObjectMapper(); + byte[] json = objectMapper.writeValueAsBytes(statusRequestDTO.getMessage()); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(statusRequestDTO.getHeader()); + g2pcUtilityClass.validateResponse(headerString , CoreConstants.REQUEST_HEADER); + String messageString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(statusRequestMessageDTO); + g2pcUtilityClass.validateResponse(messageString,CoreConstants.STATUS_REQUEST); + } + + +} + diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/1693731657.p12 b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/1693731657.p12 new file mode 100644 index 0000000..9a82799 Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/1693731657.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/application-local.yml b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/application-local.yml new file mode 100644 index 0000000..b444415 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/application-local.yml @@ -0,0 +1,132 @@ +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: not_set + error: + include-message: always + +spring.data.redis: + repositories.enabled: false + host: not_set + password: not_set + port: not_set + +client: + api_urls: + client_search_api: not_set + client_status_api: not_set + +keycloak: + from_dc: + url: not_set + clientId: not_set + clientSecret: not_set + dp: + url: not_set + username: not_set + password: not_set + master: + url: not_set + getClientUrl: not_set + clientId: not_set + clientSecret: not_set + client: + url: not_set + realm: not_set + clientId: not_set + clientSecret: not_set + +crypto: + to_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + id: not_set + from_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + +dashboard: + dp_dashboard_url: not_set + cors_origin_url: not_set + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dc: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + enabled: true + elasticsearch: + host: not_set + port: not_set + scheme: not_set \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/application.yml b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/application.yml new file mode 100644 index 0000000..b444415 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/application.yml @@ -0,0 +1,132 @@ +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: not_set + error: + include-message: always + +spring.data.redis: + repositories.enabled: false + host: not_set + password: not_set + port: not_set + +client: + api_urls: + client_search_api: not_set + client_status_api: not_set + +keycloak: + from_dc: + url: not_set + clientId: not_set + clientSecret: not_set + dp: + url: not_set + username: not_set + password: not_set + master: + url: not_set + getClientUrl: not_set + clientId: not_set + clientSecret: not_set + client: + url: not_set + realm: not_set + clientId: not_set + clientSecret: not_set + +crypto: + to_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + id: not_set + from_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + +dashboard: + dp_dashboard_url: not_set + cors_origin_url: not_set + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dc: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + enabled: true + elasticsearch: + host: not_set + port: not_set + scheme: not_set \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/farmer_on_search.p12 b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/farmer_on_search.p12 new file mode 100644 index 0000000..9a83fcb Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/farmer_on_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/farmer_search.p12 b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/farmer_search.p12 new file mode 100644 index 0000000..1873f5d Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/farmer_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/logback-spring.xml b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..529d464 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/logback-spring.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/private.p12 b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/private.p12 new file mode 100644 index 0000000..c067bff Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/private.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/schema/farmerQuerySchema.json b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/schema/farmerQuerySchema.json new file mode 100644 index 0000000..3d0a0a5 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/resources/schema/farmerQuerySchema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Query schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "query_name" : { + "type": "string" + }, + "query_params": { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/nonEmptyString", + "type": "string" + }, + "farmer_id": { + "type": "string", + "items": { + "$ref": "#/definitions/nonEmptyString", + "type": "string" + } + }, + "season": { + "$ref": "#/definitions/nonEmptyString", + "type": "string" + } + }, + "required": ["farmer_id","season"] + } + }, + "required": ["query_params"], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} + diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/webapp/WEB-INF/jsp/dashboard.jsp b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/webapp/WEB-INF/jsp/dashboard.jsp new file mode 100644 index 0000000..222cc2a --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/main/webapp/WEB-INF/jsp/dashboard.jsp @@ -0,0 +1,70 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> + + + + + Dashboard + + + +
+ +
+ + + \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/test/java/g2pc/ref/farmer/regsvc/G2pcRefFarmerRegsvcApplicationTests.java b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/test/java/g2pc/ref/farmer/regsvc/G2pcRefFarmerRegsvcApplicationTests.java new file mode 100644 index 0000000..9ce756a --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-farmer-regsvc/src/test/java/g2pc/ref/farmer/regsvc/G2pcRefFarmerRegsvcApplicationTests.java @@ -0,0 +1,63 @@ +package g2pc.ref.farmer.regsvc; + +import g2pc.core.lib.security.service.G2pTokenService; +import g2pc.core.lib.service.ElasticsearchService; +import g2pc.ref.farmer.regsvc.scheduler.Scheduler; +import lombok.extern.slf4j.Slf4j; +import org.elasticsearch.action.search.SearchResponse; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.io.IOException; + +@SpringBootTest +@Slf4j +class G2pcRefFarmerRegsvcApplicationTests { + + @Autowired + private Scheduler scheduler; + + @Autowired + G2pTokenService g2pTokenService; + + @Test + void contextLoads() { + } + + @Test + void testResponseScheduler() throws Exception { + scheduler.responseScheduler(); + } + + @Test + void testObjectMapper(){ + String jsonString = "{\"version\":\"1.0.0\",\"message_id\":\"M446-2727-0859-1434-3027\",\"message_ts\":\"2024-01-29T12:17:43+05:30\",\"action\":\"search\",\"status\":\"\",\"status_reason_code\":\"\",\"status_reason_message\":\"\",\"total_count\":0,\"completed_count\":0,\"sender_id\":\"spp.example.org\",\"receiver_id\":\"pymts.example.org\",\"is_msg_encrypted\":false,\"meta\":\"\",\"transaction_id\":\"T272-0195-3439-6563-8238\",\"correlation_id\":\"\",\"registry_type\":\"ns:FARMER_REGISTRY\",\"protocol\":\"https\",\"payload_filename\":\"file\",\"inbound_filename\":\"\",\"outbound_filename\":\"\",\"created_date\":\"2024-01-29T12:17:44+05:30\",\"last_updated_date\":\"2024-01-29T12:17:44+05:30\",\"osOwner\":[\"anonymous\"],\"osid\":\"1-a4640192-341e-4446-aa37-51e613a58e9e\"}"; + + // Create an ObjectMapper instance + ObjectMapper objectMapper = new ObjectMapper(); + + try { + // Convert JSON string to HashMap + Map resultMap = objectMapper.readValue(jsonString, new TypeReference>() {}); + + String osid = (String) resultMap.get("osid"); + // Convert Object values to String + Map stringMap = new HashMap<>(); + for (Map.Entry entry : resultMap.entrySet()) { + stringMap.put(entry.getKey(), entry.getValue().toString()); + } + + // Print the result + System.out.println(stringMap); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/.gitignore b/g2pc-reference-apps/g2pc-ref-mno-regsvc/.gitignore new file mode 100644 index 0000000..e23d88f --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/.gitignore @@ -0,0 +1,36 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +mvnw +mvnw.cmd +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +.idea/ + diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/Dockerfile b/g2pc-reference-apps/g2pc-ref-mno-regsvc/Dockerfile new file mode 100644 index 0000000..6b39e48 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/Dockerfile @@ -0,0 +1,3 @@ +FROM openjdk:17-jdk-alpine +COPY target/*.jar dp-mno-0.1.jar +ENTRYPOINT ["java","-jar","/dp-mno-0.1.jar"] \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/README.md b/g2pc-reference-apps/g2pc-ref-mno-regsvc/README.md new file mode 100644 index 0000000..e80db5b --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/README.md @@ -0,0 +1,87 @@ +# G2pc Ref Mno Reg Svc + +## JSON schema validations +This project is an implementation of the JSON Schema Draft v4, + +### Custom validation using json schema +For Mobile registry Query params are the 20% are custom changes , in which for this query param different schema has been written +in this repo. + +### Maven Dependency +```` + + com.github.erosb + everit-json-schema + 1.14.2 + +```` + + + +#### Below are some samples schema which written in this project. +```` +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Query schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "query_name" : { + "type": "string" + }, + "query_params": { + "type": "object", + "properties": { + "type": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "mobile_number": { + "type": "array", + "items": { + "$ref": "#/definitions/nonEmptyString", + "type": "string" + } + }, + "season": { + "$ref": "#/definitions/nonEmptyString", + "type": "string" + } + }, + "required": ["mobile_number","season"] + } + }, + "required": ["query_params"], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} + + + + +```` + +Using below code we can read the above schema json +```` + InputStream schemaStreamQuery = FarmerValidationServiceImpl.class.getClassLoader() + .getResourceAsStream("schema/mobileQuerySchema.json"); + JsonNode jsonNode = objectMapper.readTree(queryString); + JsonSchema schema = null; + if(schemaStreamQuery !=null){ + schema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4). + getSchema(schemaStreamQuery); + } + Set errorMessage = schema.validate(jsonNode); + List errorcombinedMessage= new ArrayList<>(); + for (ValidationMessage error : errorMessage){ + log.info("Validation errors" + error ); + errorcombinedMessage.add(new G2pcError("",error.getMessage())); + + } +```` \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/pom.xml b/g2pc-reference-apps/g2pc-ref-mno-regsvc/pom.xml new file mode 100644 index 0000000..1dc7938 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.0.12 + + + g2pc.ref.mno.regsvc + g2pc-ref-mno-regsvc + 0.0.1-SNAPSHOT + g2pc-ref-mno-regsvc + g2pc-ref-mno-regsvc + + 17 + + + + org.springframework.boot + spring-boot-starter + + + + org.projectlombok + lombok + true + + + g2pc.dp.core.lib + g2pc-dp-core-library + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.15.0 + + + org.postgresql + postgresql + 42.5.4 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springdoc + springdoc-openapi-ui + 1.6.15 + + + org.springframework.boot + spring-boot-starter-validation + + + org.apache.tomcat.embed + tomcat-embed-jasper + provided + + + javax.servlet + jstl + 1.2 + + + org.springframework.boot + spring-boot-devtools + true + + + org.elasticsearch.client + elasticsearch-rest-high-level-client + 7.10.2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/G2pcRefMnoRegsvcApplication.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/G2pcRefMnoRegsvcApplication.java new file mode 100644 index 0000000..74ef201 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/G2pcRefMnoRegsvcApplication.java @@ -0,0 +1,24 @@ +package g2pc.ref.mno.regsvc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) +@ComponentScan({"g2pc.core.lib", + "g2pc.dp.core.lib", + "g2pc.ref.mno.regsvc", + "g2pc.dp.core.lib.service", + "g2pc.dp.core.lib.repository", + "g2pc.core.lib.security.serviceImpl"}) +@EnableScheduling +public class G2pcRefMnoRegsvcApplication { + + public static void main(String[] args) { + SpringApplication.run(G2pcRefMnoRegsvcApplication.class, args); + } + +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/config/CorsConfig.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/config/CorsConfig.java new file mode 100644 index 0000000..6dab4e5 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/config/CorsConfig.java @@ -0,0 +1,21 @@ +package g2pc.ref.mno.regsvc.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Value("${dashboard.cors_origin_url}") + private String corsOriginUrl; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins(corsOriginUrl) + .allowedMethods("*") + .allowedHeaders("*"); + } +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/config/ObjectMapperConfig.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/config/ObjectMapperConfig.java new file mode 100644 index 0000000..29e2fd7 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/config/ObjectMapperConfig.java @@ -0,0 +1,23 @@ +package g2pc.ref.mno.regsvc.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.ref.mno.regsvc.dto.request.QueryMobileDTO; +import g2pc.ref.mno.regsvc.dto.request.QueryParamsMobileDTO; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ObjectMapperConfig { + + @Bean + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, + QueryMobileDTO.class, + QueryParamsMobileDTO.class); + return objectMapper; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/constants/Constants.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/constants/Constants.java new file mode 100644 index 0000000..1f96fbd --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/constants/Constants.java @@ -0,0 +1,25 @@ +package g2pc.ref.mno.regsvc.constants; + +public class Constants { + + private Constants() { + } + + public static final String SEARCH_REQUEST_RECEIVED = "Search request received successfully"; + + public static final String INVALID_RESPONSE = "Invalid Response received from server"; + + public static final String CONFLICT = "CONFLICT"; + + public static final String INVALID_AUTHORIZATION = "Invalid Authorization"; + + public static final String CACHE_KEY_SEARCH_STRING = "request-mno*"; + + public static final String CACHE_KEY_STRING = "request-mno-"; + + public static final String CONFIGURATION_MISMATCH_ERROR = "Configurations are not matching "; + + public static final String STATUS_CACHE_KEY_STRING = "status-request-farmer-"; + + public static final String STATUS_CACHE_KEY_SEARCH_STRING = "status-request-farmer*"; +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/rest/DpDashboardController.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/rest/DpDashboardController.java new file mode 100644 index 0000000..c191b1b --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/rest/DpDashboardController.java @@ -0,0 +1,17 @@ +package g2pc.ref.mno.regsvc.controller.rest; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class DpDashboardController { + + private static final String DASHBOARD_URL = "http://localhost:3005/d-solo/c766225a-d5cf-4c9f-99a7-6f8291f407eb/dp-dashboard?orgId=1&refresh=5s&from=1701984074137&to=1702005674137&panelId=1"; + + @GetMapping("/dashboard") + public String dashboard(Model model) { + model.addAttribute("dashboardUrl", DASHBOARD_URL); + return "dashboard"; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/rest/RegistryController.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/rest/RegistryController.java new file mode 100644 index 0000000..b4aeba1 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/rest/RegistryController.java @@ -0,0 +1,202 @@ +package g2pc.ref.mno.regsvc.controller.rest; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.AcknowledgementDTO; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.exceptionhandler.ErrorResponse; +import g2pc.core.lib.exceptionhandler.ValidationErrorResponse; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.dp.core.lib.repository.MsgTrackerRepository; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.dp.core.lib.utils.DpCommonUtils; +import g2pc.ref.mno.regsvc.constants.Constants; +import g2pc.ref.mno.regsvc.service.DpSftpPushUpdateService; +import g2pc.ref.mno.regsvc.service.MobileValidationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +/** + * The type Registry controller. + */ +@RestController +@Slf4j +@RequestMapping(produces = "application/json") +@Tag(name = "Provider", description = "Provider APIs") +public class RegistryController { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + MobileValidationService mobileValidationService; + + @Autowired + private DpCommonUtils dpCommonUtils; + + @Autowired + private MsgTrackerRepository msgTrackerRepository; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private DpSftpPushUpdateService dpSftpPushUpdateService; + + /** + * Get search request from DC + * + * @param requestString required + * @return Search request received acknowledgement + * @throws JsonProcessingException the json processing exception + * @throws ResponseStatusException the response status exception + * @throws G2pcValidationException the validation exception + */ + @Operation(summary = "Receive search request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @SuppressWarnings("unchecked") + @PostMapping("/private/api/v1/registry/search") + public AcknowledgementDTO registerCandidateInformation(@RequestBody String requestString) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + dpCommonUtils.handleToken(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); + RequestMessageDTO messageDTO = null; + + Map metaData = (Map) requestDTO.getHeader().getMeta().getData(); + messageDTO = mobileValidationService.signatureValidation(metaData, requestDTO); + requestDTO.setMessage(messageDTO); + String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); + + try { + mobileValidationService.validateRequestDTO(requestDTO); + return requestHandlerService.buildCacheRequest( + objectMapper.writeValueAsString(requestDTO), cacheKey, CoreConstants.SEND_PROTOCOL_HTTPS, sunbirdEnabled); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.MULTI_STATUS, e.getMessage()); + } + } + + + /** + * Handle validation exception error response. + * + * @param ex the ValidationException + * @return the error response + */ + @ExceptionHandler(value + = G2pcValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ValidationErrorResponse + handleValidationException( + G2pcValidationException ex) { + return new ValidationErrorResponse( + ex.getG2PcErrorList()); + } + + /** + * Handle validation exception error response. + * + * @param ex the ValidationException + * @return the error response + */ + @ExceptionHandler(value = G2pHttpException.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public ErrorResponse handleG2pHttpStatusException(G2pHttpException ex) { + return new ErrorResponse(ex.getG2PcError()); + + } + + /** + * Clear message tracker DB + * Clear Redis cache + */ + @GetMapping("/private/api/v1/registry/clear-db") + public void clearDb() throws G2pHttpException, IOException { + dpCommonUtils.handleToken(); + msgTrackerRepository.deleteAll(); + log.info("DP-2 DB cleared"); + Set keys = redisTemplate.keys("*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); + } + log.info("DP-2 Redis cache cleared"); + } + + @Operation(summary = "Receive status request") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = Constants.SEARCH_REQUEST_RECEIVED), + @ApiResponse(responseCode = "401", description = Constants.INVALID_AUTHORIZATION), + @ApiResponse(responseCode = "403", description = Constants.INVALID_RESPONSE), + @ApiResponse(responseCode = "500", description = Constants.CONFLICT)}) + @SuppressWarnings("unchecked") + @PostMapping("/private/api/v1/registry/txn/status") + public AcknowledgementDTO handleStatusRequest(@RequestBody String requestString) throws Exception { + dpCommonUtils.handleToken(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + StatusRequestDTO statusRequestDTO = objectMapper.readerFor(StatusRequestDTO.class). + readValue(requestString); + StatusRequestMessageDTO statusRequestMessageDTO = null; + + Map metaData = (Map) statusRequestDTO.getHeader().getMeta().getData(); + + statusRequestMessageDTO = mobileValidationService.signatureValidation(metaData, statusRequestDTO); + statusRequestDTO.setMessage(statusRequestMessageDTO); + String cacheKey = Constants.STATUS_CACHE_KEY_STRING + statusRequestMessageDTO.getTransactionId(); + try { + mobileValidationService.validateStatusRequestDTO(statusRequestDTO); + return requestHandlerService.buildCacheStatusRequest( + objectMapper.writeValueAsString(statusRequestDTO), cacheKey, CoreConstants.SEND_PROTOCOL_HTTPS); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } + + @GetMapping(value = "/dashboard/sftp/dp2/data", produces = "text/event-stream") + public SseEmitter sseEmitterFirstPanel() { + return dpSftpPushUpdateService.register(); + } +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/sftp/DcSftpListener.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/sftp/DcSftpListener.java new file mode 100644 index 0000000..b3b2887 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/controller/sftp/DcSftpListener.java @@ -0,0 +1,93 @@ +package g2pc.ref.mno.regsvc.controller.sftp; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.dto.common.header.HeaderDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.ref.mno.regsvc.constants.Constants; +import g2pc.ref.mno.regsvc.service.MobileValidationService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; +import org.springframework.web.server.ResponseStatusException; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +@Configuration +@Slf4j +public class DcSftpListener { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Value("${sftp.listener.local.inbound_directory}") + private String sftpLocalDirectoryInbound; + + @Value("${sftp.listener.local.outbound_directory}") + private String sftpLocalDirectoryOutbound; + + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + MobileValidationService mobileValidationService; + + @SuppressWarnings("unchecked") + @ServiceActivator(inputChannel = "sftpInbound") + public void handleMessageInbound(Message message) { + try { + File file = message.getPayload(); + log.info("Received Message from inbound directory of dp-2: {}", file.getName()); + if (ObjectUtils.isNotEmpty(file) && file.getName().contains(".json")) { + String requestString = new String(Files.readAllBytes(file.toPath())); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, + ResponseHeaderDTO.class, HeaderDTO.class); + + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class). + readValue(requestString); + RequestMessageDTO messageDTO; + + Map metaData = (Map) requestDTO.getHeader().getMeta().getData(); + + messageDTO = mobileValidationService.signatureValidation(metaData, requestDTO); + requestDTO.setMessage(messageDTO); + String cacheKey = Constants.CACHE_KEY_STRING + messageDTO.getTransactionId(); + try { + mobileValidationService.validateRequestDTO(requestDTO); + requestHandlerService.buildCacheRequest( + objectMapper.writeValueAsString(requestDTO), cacheKey, CoreConstants.SEND_PROTOCOL_SFTP, sunbirdEnabled); + } catch (G2pcValidationException e) { + throw new G2pcValidationException(e.getG2PcErrorList()); + } catch (JsonProcessingException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + } + Files.deleteIfExists(Path.of(sftpLocalDirectoryInbound + "/" + file.getName())); + } catch (Exception e) { + log.error("Error: ", e); + } + } + + @ServiceActivator(inputChannel = "errorChannel") + public void handleError(Message message) { + Throwable error = (Throwable) message.getPayload(); + log.error("Handling ERROR: {}", error.getMessage()); + } +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/SftpDpData.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/SftpDpData.java new file mode 100644 index 0000000..7aafbd6 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/SftpDpData.java @@ -0,0 +1,19 @@ +package g2pc.ref.mno.regsvc.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SftpDpData { + + private String dpType; + + private String messageTs; + + private String transactionId; + + private String fileName; +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/request/QueryMobileDTO.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/request/QueryMobileDTO.java new file mode 100644 index 0000000..0c0a981 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/request/QueryMobileDTO.java @@ -0,0 +1,19 @@ +package g2pc.ref.mno.regsvc.dto.request; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class QueryMobileDTO{ + + @JsonProperty("query_params") + private QueryParamsMobileDTO queryParams; +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/request/QueryParamsMobileDTO.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/request/QueryParamsMobileDTO.java new file mode 100644 index 0000000..b0319f1 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/request/QueryParamsMobileDTO.java @@ -0,0 +1,24 @@ +package g2pc.ref.mno.regsvc.dto.request; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class QueryParamsMobileDTO { + + @JsonProperty("mobile_number") + private List mobileNumber; + + @JsonProperty("season") + private String season; +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/response/DataMobileDTO.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/response/DataMobileDTO.java new file mode 100644 index 0000000..882fa81 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/response/DataMobileDTO.java @@ -0,0 +1,18 @@ +package g2pc.ref.mno.regsvc.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +public class DataMobileDTO{ + + @JsonProperty("reg_records") + private List regRecords; +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/response/RegRecordMobileDTO.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/response/RegRecordMobileDTO.java new file mode 100644 index 0000000..65c9f5d --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/dto/response/RegRecordMobileDTO.java @@ -0,0 +1,30 @@ +package g2pc.ref.mno.regsvc.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Getter +@Setter +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class RegRecordMobileDTO { + + @JsonProperty("farmer_id") + private String farmerId; + + @JsonProperty("farmer_name") + private String farmerName; + + @JsonProperty("season") + private String season; + + @JsonProperty("mobile_number") + private String mobileNumber; + + @JsonProperty("mobile_status") + private String mobileStatus; + + @JsonProperty("created_date") + private String createdDate; +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/scheduler/Scheduler.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/scheduler/Scheduler.java new file mode 100644 index 0000000..0e1892e --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/scheduler/Scheduler.java @@ -0,0 +1,147 @@ +package g2pc.ref.mno.regsvc.scheduler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.dto.common.cache.CacheDTO; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.common.header.ResponseHeaderDTO; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.enums.HeaderStatusENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.dp.core.lib.entity.MsgTrackerEntity; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.dp.core.lib.service.ResponseBuilderService; +import g2pc.dp.core.lib.service.TxnTrackerDbService; +import g2pc.dp.core.lib.service.TxnTrackerRedisService; +import g2pc.dp.core.lib.utils.DpCommonUtils; +import g2pc.ref.mno.regsvc.constants.Constants; +import g2pc.ref.mno.regsvc.service.MobileResponseBuilderService; +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.List; + +import org.springframework.core.io.ResourceLoader; + +@Slf4j +@Service +public class Scheduler { + + @Value("${sunbird.enabled}") + private Boolean sunbirdEnabled; + + @Value("${client.api_urls.client_search_api}") + String onSearchURL; + + @Value("${client.api_urls.client_status_api}") + String onStatusURL; + + @Autowired + private RequestHandlerService requestHandlerService; + + @Autowired + private ResponseBuilderService responseBuilderService; + + @Autowired + private ResourceLoader resourceLoader; + @Autowired + private MobileResponseBuilderService mobileResponseBuilderService; + + @Value("${keycloak.from-dc.client-id}") + private String dcClientId; + + @Value("${keycloak.from-dc.client-secret}") + private String dcClientSecret; + + @Value("${keycloak.from-dc.url}") + private String keyClockClientTokenUrl; + + @Autowired + private TxnTrackerRedisService txnTrackerRedisService; + + @Autowired + private TxnTrackerDbService txnTrackerDbService; + + @Value("${crypto.to_dc.id}") + private String dpId; + + @Value("${crypto.to_dc.key_path}") + private String farmerKeyPath; + + @Autowired + private DpCommonUtils dpCommonUtils; + + /** + * This method is a scheduled task that runs every minute. + * It retrieves data from a Redis cache, processes it, and sends a response. + * If the status of the data is 'PDNG', it processes the data and updates the status to 'SUCC'. + * If an exception occurs during the process, it logs the exception message. + * + * @throws IOException if an I/O error occurs + */ + @SuppressWarnings("unchecked") + @Scheduled(cron = "0 */1 * ? * *")// runs every 1 min. + @Transactional + public void responseScheduler() throws IOException { + try { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(RequestHeaderDTO.class, ResponseHeaderDTO.class); + + List cacheKeysList = txnTrackerRedisService.getCacheKeys(Constants.CACHE_KEY_SEARCH_STRING); + for (String cacheKey : cacheKeysList) { + String requestData = txnTrackerRedisService.getRequestData(cacheKey); + CacheDTO cacheDTO = objectMapper.readerFor(CacheDTO.class).readValue(requestData); + if (cacheDTO.getStatus().equals(HeaderStatusENUM.PDNG.toValue())) { + String protocol = cacheDTO.getProtocol(); + RequestDTO requestDTO = objectMapper.readerFor(RequestDTO.class).readValue(cacheDTO.getData()); + MsgTrackerEntity msgTrackerEntity = txnTrackerDbService.saveRequestDetails(requestDTO, protocol, sunbirdEnabled); + List queryDTOList = msgTrackerEntity.getTxnTrackerEntityList().stream() + .map(txnTrackerEntity -> { + try { + return objectMapper.readValue(txnTrackerEntity.getQuery(), QueryDTO.class); + } catch (JsonProcessingException e) { + return null; + } + }).toList(); + List refRecordsStringsList = mobileResponseBuilderService.getRegMobileRecords(queryDTOList); + G2pcError g2pcError = responseBuilderService.buildOnSearchScheduler(refRecordsStringsList, cacheDTO, sunbirdEnabled); + log.info("on-search database updation response from sunbird - " + g2pcError.getCode()); + if (!g2pcError.getCode().equals(HttpStatus.OK.toString())) { + throw new G2pHttpException(g2pcError); + } else { + txnTrackerRedisService.updateRequestDetails(cacheKey, HeaderStatusENUM.SUCC.toValue(), cacheDTO); + } + } + } + List statusCacheKeysList = txnTrackerRedisService.getCacheKeys(Constants.STATUS_CACHE_KEY_SEARCH_STRING); + for (String cacheKey : statusCacheKeysList) { + String requestData = txnTrackerRedisService.getRequestData(cacheKey); + CacheDTO cacheDTO = objectMapper.readerFor(CacheDTO.class).readValue(requestData); + if (cacheDTO.getStatus().equals(HeaderStatusENUM.PDNG.toValue())) { + StatusRequestDTO statusRequestDTO = objectMapper.readerFor(StatusRequestDTO.class).readValue(cacheDTO.getData()); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.convertValue(statusRequestDTO.getMessage(), StatusRequestMessageDTO.class); + G2pcError g2pcError = responseBuilderService.buildOnStatusScheduler(cacheDTO); + log.info("on-status database updation response from sunbird - " + g2pcError.getCode()); + if (!g2pcError.getCode().equals(HttpStatus.OK.toString())) { + throw new G2pHttpException(g2pcError); + } else { + txnTrackerDbService.updateMessageTrackerStatusDb(statusRequestMessageDTO.getTransactionId()); + txnTrackerRedisService.updateRequestDetails(cacheKey, HeaderStatusENUM.SUCC.toValue(), cacheDTO); + } + } + } + } catch (Exception ex) { + log.error("Exception in responseScheduler: {}", ex.getMessage()); + } + } +} \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/DpSftpPushUpdateService.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/DpSftpPushUpdateService.java new file mode 100644 index 0000000..3b71fd6 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/DpSftpPushUpdateService.java @@ -0,0 +1,10 @@ +package g2pc.ref.mno.regsvc.service; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +public interface DpSftpPushUpdateService { + + SseEmitter register(); + + void pushUpdate(Object update); +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/MobileResponseBuilderService.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/MobileResponseBuilderService.java new file mode 100644 index 0000000..093334b --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/MobileResponseBuilderService.java @@ -0,0 +1,11 @@ +package g2pc.ref.mno.regsvc.service; + + +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import java.io.IOException; +import java.util.List; + +public interface MobileResponseBuilderService { + + List getRegMobileRecords(List queryDTOList) throws IOException; +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/MobileValidationService.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/MobileValidationService.java new file mode 100644 index 0000000..21808a2 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/service/MobileValidationService.java @@ -0,0 +1,24 @@ +package g2pc.ref.mno.regsvc.service; + +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.exceptions.G2pcValidationException; +import java.io.IOException; +import java.util.Map; + +/** + * The interface Mobile validation service. + */ +public interface MobileValidationService { + + void validateRequestDTO (RequestDTO requestDTO) throws Exception; + + RequestMessageDTO signatureValidation(Map metaData, RequestDTO requestDTO ) throws Exception; + + StatusRequestMessageDTO signatureValidation(Map metaData, StatusRequestDTO requestDTO) throws Exception ; + + void validateStatusRequestDTO (StatusRequestDTO requestDTO) throws IOException, G2pcValidationException; + +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/DpSftpPushUpdateServiceImpl.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/DpSftpPushUpdateServiceImpl.java new file mode 100644 index 0000000..dd07371 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/DpSftpPushUpdateServiceImpl.java @@ -0,0 +1,50 @@ +package g2pc.ref.mno.regsvc.serviceimpl; + +import g2pc.ref.mno.regsvc.dto.SftpDpData; +import g2pc.ref.mno.regsvc.service.DpSftpPushUpdateService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class DpSftpPushUpdateServiceImpl implements DpSftpPushUpdateService { + + private final List emitters = new ArrayList<>(); + + public SseEmitter register() { + int minutes = 15; + long timeout = (long) minutes * 60000; + SseEmitter emitter = new SseEmitter(timeout); + this.emitters.add(emitter); + emitter.onCompletion(() -> this.emitters.remove(emitter)); + emitter.onTimeout(() -> this.emitters.remove(emitter)); + log.info("SSE emitter registered" + emitter); + return emitter; + } + + public void pushUpdate(Object update) { + List deadEmitters = new ArrayList<>(); + this.emitters.forEach(emitter -> { + try { + emitter.send(update); + } catch (IOException e) { + deadEmitters.add(emitter); + } + }); + this.emitters.removeAll(deadEmitters); + } + + public SftpDpData getSftpDpData(String dpType, String messageTs, String transactionId, String filename){ + SftpDpData sftpDpData = new SftpDpData(); + sftpDpData.setDpType(dpType); + sftpDpData.setMessageTs(messageTs); + sftpDpData.setTransactionId(transactionId); + sftpDpData.setFileName(filename); + return sftpDpData; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/MobileResponseBuilderServiceImpl.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/MobileResponseBuilderServiceImpl.java new file mode 100644 index 0000000..e9b1cfd --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/MobileResponseBuilderServiceImpl.java @@ -0,0 +1,51 @@ +package g2pc.ref.mno.regsvc.serviceimpl; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.config.G2pUnirestHelper; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.ref.mno.regsvc.dto.response.RegRecordMobileDTO; +import g2pc.ref.mno.regsvc.service.MobileResponseBuilderService; +import kong.unirest.HttpResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class MobileResponseBuilderServiceImpl implements MobileResponseBuilderService { + + @Value("${client.api_urls.mno_info_url}") + String mobileInfoURL; + + @Autowired + G2pUnirestHelper g2pUnirestHelper; + + /** + * Get farmer records information string from API + * + * @param queryDTOList required + * @return list of mobile records + */ + @Override + public List getRegMobileRecords(List queryDTOList) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + log.info("getRegMobileRecords : {}", objectMapper.writeValueAsString(queryDTOList)); + List regMnoRecordsList = new ArrayList<>(); + String uri = mobileInfoURL; + log.info("mobileInfoURL : {}", uri); + HttpResponse response = g2pUnirestHelper.g2pPost(uri) + .body(objectMapper.writeValueAsString(queryDTOList)) + .asString(); + List regRecordMobileDTOList = objectMapper.readValue(response.getBody(), + new TypeReference<>() {}); + for (RegRecordMobileDTO regRecordMobileDTO : regRecordMobileDTOList) { + regMnoRecordsList.add(objectMapper.writeValueAsString(regRecordMobileDTO)); + } + return regMnoRecordsList; + } +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/MobileValidationServiceImpl.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/MobileValidationServiceImpl.java new file mode 100644 index 0000000..fa34b31 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/java/g2pc/ref/mno/regsvc/serviceimpl/MobileValidationServiceImpl.java @@ -0,0 +1,305 @@ +package g2pc.ref.mno.regsvc.serviceimpl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import g2pc.core.lib.constants.CoreConstants; +import g2pc.core.lib.constants.G2pSecurityConstants; +import g2pc.core.lib.dto.common.header.RequestHeaderDTO; +import g2pc.core.lib.dto.search.message.request.QueryDTO; +import g2pc.core.lib.dto.search.message.request.RequestDTO; +import g2pc.core.lib.dto.search.message.request.RequestMessageDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestDTO; +import g2pc.core.lib.dto.status.message.request.StatusRequestMessageDTO; +import g2pc.core.lib.enums.ExceptionsENUM; +import g2pc.core.lib.exceptions.G2pHttpException; +import g2pc.core.lib.exceptions.G2pcError; +import g2pc.core.lib.exceptions.G2pcValidationException; +import g2pc.core.lib.security.service.AsymmetricSignatureService; +import g2pc.core.lib.security.service.G2pEncryptDecrypt; +import g2pc.core.lib.security.service.G2pcUtilityClass; +import g2pc.dp.core.lib.service.RequestHandlerService; +import g2pc.ref.mno.regsvc.constants.Constants; +import g2pc.ref.mno.regsvc.dto.request.QueryMobileDTO; +import g2pc.ref.mno.regsvc.dto.request.QueryParamsMobileDTO; +import g2pc.ref.mno.regsvc.service.MobileValidationService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.SignatureException; +import java.util.*; +/** + * The type Mobile validation service. + */ +@Service +@Slf4j +public class MobileValidationServiceImpl implements MobileValidationService { + + /** + * The Request handler service. + */ + @Autowired + RequestHandlerService requestHandlerService; + + @Autowired + G2pEncryptDecrypt encryptDecrypt; + + @Value("${crypto.from_dc.support_encryption}") + private boolean isEncrypt; + + @Value("${crypto.from_dc.support_signature}") + private boolean isSign; + + @Value("${crypto.from_dc.password}") + private String p12Password; + @Autowired + private AsymmetricSignatureService asymmetricSignatureService; + @Autowired + private ResourceLoader resourceLoader; + + @Value("${crypto.from_dc.key_path}") + private String mobileKeyPath; + + @Autowired + G2pcUtilityClass g2pcUtilityClass; + /** + * Method to validate Request dto + * @param requestDTO the request dto + * @throws Exception required + */ + @Override + public void validateRequestDTO(RequestDTO requestDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerSubtypes(QueryDTO.class, + QueryMobileDTO.class, QueryParamsMobileDTO.class); + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + RequestMessageDTO messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(requestDTO.getHeader()); + g2pcUtilityClass.validateResponse(headerString , CoreConstants.REQUEST_HEADER); + String messageString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(messageDTO); + g2pcUtilityClass.validateResponse(messageString,CoreConstants.SEARCH_REQUEST); + } + + /** + * Method to validate signature and encryption of request dto + * @param metaData required + * @param requestDTO required + * @return RequestMessageDTO request message dto + * @throws Exception required + */ + @Override + public RequestMessageDTO signatureValidation(Map metaData, RequestDTO requestDTO) throws Exception { + log.info("Is encrypted ? -> " + isEncrypt); + log.info("Is signed ? -> " + isSign); + ObjectMapper objectMapper = new ObjectMapper(); + RequestMessageDTO messageDTO; + Boolean isMsgEncrypted = requestDTO.getHeader().getIsMsgEncrypted(); + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource= resourceLoader.getResource(mobileKeyPath); + InputStream fis = resource.getInputStream(); + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = requestDTO.getMessage().toString(); + String data = requestHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(isMsgEncrypted){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException | InvalidAlgorithmParameterException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(RequestMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = requestHeaderString+messageString; + log.info("Signature ->"+requestSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = requestDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException | InvalidAlgorithmParameterException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(RequestMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, RequestMessageDTO.class); + } + } + return messageDTO; + } + + @Override + public StatusRequestMessageDTO signatureValidation(Map metaData, StatusRequestDTO requestDTO) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + StatusRequestMessageDTO messageDTO; + Boolean isMsgEncrypted = requestDTO.getHeader().getIsMsgEncrypted(); + if(isSign){ + if(!metaData.get(CoreConstants.IS_SIGN).equals(true)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(), Constants.CONFIGURATION_MISMATCH_ERROR)); + } + Resource resource = resourceLoader.getResource(mobileKeyPath); + InputStream fis = resource.getInputStream(); + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = requestDTO.getMessage().toString(); + String data = requestHeaderString+messageString; + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ->"+e.getMessage())); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), e.getMessage())); + } + if(isMsgEncrypted){ + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString, G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException | InvalidAlgorithmParameterException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusRequestMessageDTO.class). + readValue(deprecatedMessageString); + } else { + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + String requestHeaderString = objectMapper.writeValueAsString(requestDTO.getHeader()); + String requestSignature = requestDTO.getSignature(); + String messageString = objectMapper.writeValueAsString(messageDTO); + String data = requestHeaderString+messageString; + log.info("Signature ->"+requestSignature); + try{if(! asymmetricSignatureService.verifySignature(data.getBytes(), Base64.getDecoder().decode(requestSignature) , fis ,p12Password) ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + }}catch(SignatureException e){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + catch(IOException e){ + log.info("Rejecting the on-search request in farmer as signature is not valid"); + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_SIGNATURE_INVALID.toValue(), "signature is not valid ")); + } + + } + } else { + if(!metaData.get(CoreConstants.IS_SIGN).equals(false)){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + if(isEncrypt){ + if(!isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + String messageString = requestDTO.getMessage().toString(); + String deprecatedMessageString; + try{ + deprecatedMessageString= encryptDecrypt.g2pDecrypt(messageString,G2pSecurityConstants.SECRET_KEY); + } catch (RuntimeException | InvalidAlgorithmParameterException e ){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_ENCRYPTION_INVALID.toValue(), "Error in Encryption/Decryption")); + } + log.info("Decrypted Message string ->"+deprecatedMessageString); + messageDTO = objectMapper.readerFor(StatusRequestMessageDTO.class). + readValue(deprecatedMessageString); + + }else{ + if(isMsgEncrypted){ + throw new G2pHttpException(new G2pcError(ExceptionsENUM.ERROR_VERSION_NOT_VALID.toValue(),Constants.CONFIGURATION_MISMATCH_ERROR)); + } + byte[] json = objectMapper.writeValueAsBytes(requestDTO.getMessage()); + messageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + } + } + requestDTO.setMessage(messageDTO); + return messageDTO; + } + + @Override + public void validateStatusRequestDTO(StatusRequestDTO statusRequestDTO) throws IOException, G2pcValidationException { + ObjectMapper objectMapper = new ObjectMapper(); + byte[] json = objectMapper.writeValueAsBytes(statusRequestDTO.getMessage()); + StatusRequestMessageDTO statusRequestMessageDTO = objectMapper.readValue(json, StatusRequestMessageDTO.class); + String headerString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(statusRequestDTO.getHeader()); + g2pcUtilityClass.validateResponse(headerString , CoreConstants.REQUEST_HEADER); + String messageString = new ObjectMapper() + .writerWithDefaultPrettyPrinter() + .writeValueAsString(statusRequestMessageDTO); + g2pcUtilityClass.validateResponse(messageString,CoreConstants.STATUS_REQUEST); + } + + +} diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/1693731657.p12 b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/1693731657.p12 new file mode 100644 index 0000000..9a82799 Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/1693731657.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/application-local.yml b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/application-local.yml new file mode 100644 index 0000000..f1a7ca7 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/application-local.yml @@ -0,0 +1,131 @@ +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: not_set + error: + include-message: always + +spring.data.redis: + repositories.enabled: false + host: not_set + password: not_set@99223 + port: not_set + +client: + api_urls: + client_search_api: not_set + mno_info_url: not_set + client_status_api: not_set + +keycloak: + from-dc: + url: not_set + client-id: not_set + client-secret: not_set + dp: + url: not_set + username: not_set + password: not_set + master: + url: not_set + getClientUrl: not_set + clientId: not_set + clientSecret: not_set + client: + url: not_set + realm: not_set + clientId: not_set + clientSecret: not_set + +crypto: + to_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + id: not_set + from_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + +dashboard: + dp_dashboard_url: not_set + cors_origin_url: not_set + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dc: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + outbound_directory: not_set + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + enabled: false + elasticsearch: + host: not_set + port: not_set + scheme: not_set \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/application.yml b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/application.yml new file mode 100644 index 0000000..f1a7ca7 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/application.yml @@ -0,0 +1,131 @@ +spring: + mvc: + view: + prefix: /WEB-INF/jsp/ + suffix: .jsp + + pathmatch: + matching-strategy: ANT_PATH_MATCHER + datasource: + driverClassName: org.postgresql.Driver + url: not_set + username: not_set + password: not_set + + hikari: + data-source-properties: + stringtype: unspecified + cachePrepStmts: true + prepStmtCacheSize: 250 + prepStmtCacheSqlLimit: 2048 + useServerPrepStmts: true + useLocalSessionState: true + rewriteBatchedStatements: true + cacheResultSetMetadata: true + cacheServerConfiguration: true + maintainTimeStats: false + maximum-pool-size: 5 + connection-timeout: 5000 + jpa: + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + dialect: org.hibernate.dialect.PostgreSQLDialect + hibernate.ddl-auto: none + show-sql: false + open-in-view: false + generate-ddl: false + autoconfigure: + exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration + + devtools: + restart: + additional-paths: src/main/webapp + exclude: static/**,public/** + +server: + port: not_set + error: + include-message: always + +spring.data.redis: + repositories.enabled: false + host: not_set + password: not_set@99223 + port: not_set + +client: + api_urls: + client_search_api: not_set + mno_info_url: not_set + client_status_api: not_set + +keycloak: + from-dc: + url: not_set + client-id: not_set + client-secret: not_set + dp: + url: not_set + username: not_set + password: not_set + master: + url: not_set + getClientUrl: not_set + clientId: not_set + clientSecret: not_set + client: + url: not_set + realm: not_set + clientId: not_set + clientSecret: not_set + +crypto: + to_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + id: not_set + from_dc: + support_encryption: true + support_signature: true + password: not_set + key_path: not_set + +dashboard: + dp_dashboard_url: not_set + cors_origin_url: not_set + +sftp: + listener: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + inbound_directory: not_set + outbound_directory: not_set + local: + inbound_directory: not_set + outbound_directory: not_set + + dc: + host: not_set + port: not_set + user: not_set + password: not_set + remote: + outbound_directory: not_set + +sunbird: + api_urls: + response_data_api: not_set + response_tracker_api: not_set + enabled: false + elasticsearch: + host: not_set + port: not_set + scheme: not_set \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/mobile_on_search.p12 b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/mobile_on_search.p12 new file mode 100644 index 0000000..43fe9ea Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/mobile_on_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/mobile_search.p12 b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/mobile_search.p12 new file mode 100644 index 0000000..372e8c6 Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/mobile_search.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/private.p12 b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/private.p12 new file mode 100644 index 0000000..c067bff Binary files /dev/null and b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/private.p12 differ diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/schema/mobileQuerySchema.json b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/schema/mobileQuerySchema.json new file mode 100644 index 0000000..c8b7b09 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/resources/schema/mobileQuerySchema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft-04/schema#", + "$id": "https://example.com/message.schema.json", + "title": "Query schema", + "description": "", + "additionalProperties": false, + "type": "object", + "properties": { + "query_name" : { + "type": "string" + }, + "query_params": { + "type": "object", + "properties": { + "type": { + "type": "string", + "$ref": "#/definitions/nonEmptyString" + }, + "mobile_number": { + "type": ["string"], + "items": { + "$ref": "#/definitions/nonEmptyString", + "type": "string" + } + }, + "season": { + "$ref": "#/definitions/nonEmptyString", + "type": "string" + } + }, + "required": ["mobile_number","season"] + } + }, + "required": ["query_params"], + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + } + } +} + diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/webapp/WEB-INF/jsp/dashboard.jsp b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/webapp/WEB-INF/jsp/dashboard.jsp new file mode 100644 index 0000000..222cc2a --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/main/webapp/WEB-INF/jsp/dashboard.jsp @@ -0,0 +1,70 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> + + + + + Dashboard + + + +
+ +
+ + + \ No newline at end of file diff --git a/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/test/java/g2pc/ref/mno/regsvc/G2pcRefMnoRegsvcApplicationTests.java b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/test/java/g2pc/ref/mno/regsvc/G2pcRefMnoRegsvcApplicationTests.java new file mode 100644 index 0000000..bf76d24 --- /dev/null +++ b/g2pc-reference-apps/g2pc-ref-mno-regsvc/src/test/java/g2pc/ref/mno/regsvc/G2pcRefMnoRegsvcApplicationTests.java @@ -0,0 +1,28 @@ +package g2pc.ref.mno.regsvc; + +import com.fasterxml.jackson.core.JsonProcessingException; +import g2pc.ref.mno.regsvc.scheduler.Scheduler; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.io.IOException; + +@SpringBootTest +class G2pcRefMnoRegsvcApplicationTests { + + @Autowired + private Scheduler scheduler; + + @Test + void contextLoads() { + } + + @Test + void testResponseScheduler() throws IOException { + scheduler.responseScheduler(); + } + + + +}