From 367f4956ce1ad871e74e32537486c65a568ca5a8 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Thu, 20 Mar 2025 19:53:25 +0530 Subject: [PATCH] Done ticket GEPAFINBE-190 Updated the password handling code for login odessa api and for all other api's. --- .../constants/GepafinConstant.java | 3 + .../tendermanagement/dao/AppointmentDao.java | 173 +++++++++++++----- src/main/resources/message_en.properties | 2 + src/main/resources/message_it.properties | 4 +- 4 files changed, 134 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 87d6ad3e..b192fba5 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -496,6 +496,9 @@ public class GepafinConstant { public static final String PDF_TRUE="PDF_TRUE"; public static final String PDF_FALSE="PDF_FALSE"; + + public static final String PASSWORD_EXPIRED = "PasswordExpired"; + public static final String PASSWORD_EXPIRED_LOGIN_TO_ODESSA = "password.expired.for.login.to.odessa"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java index 876ac53d..5a8ee2be 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -14,6 +14,7 @@ import net.gepafin.tendermanagement.config.jwt.TokenProvider; import net.gepafin.tendermanagement.constants.AppointmentApiConstant; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.ApplicationEntity; +import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity; import net.gepafin.tendermanagement.entities.CompanyEntity; import net.gepafin.tendermanagement.entities.DocumentEntity; import net.gepafin.tendermanagement.entities.HubEntity; @@ -39,6 +40,7 @@ import net.gepafin.tendermanagement.repositories.UserRepository; import net.gepafin.tendermanagement.service.ApplicationService; import net.gepafin.tendermanagement.service.CompanyService; import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService; +import net.gepafin.tendermanagement.service.impl.ApplicationEvaluationServiceImpl; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; @@ -60,6 +62,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -138,6 +141,9 @@ public class AppointmentDao { @Autowired private UserRepository userRepository; + @Autowired + private ApplicationEvaluationServiceImpl applicationEvaluationService; + private final Map executorMap = new ConcurrentHashMap<>(); private final ConcurrentHashMap threadForDocumentMap = new ConcurrentHashMap<>(); @@ -162,10 +168,85 @@ public class AppointmentDao { applicationRepository.save(application); // Start async processing + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + loginToOdessa(hub, application); startAsyncNdgProcessing(applicationId); - throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); } + private HubEntity loginToOdessa(HubEntity hub, ApplicationEntity application) { + try { + //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call + String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); + log.info("Got the auth for login to odessa {}", authJwtToken); + hub.setAuthToken(authJwtToken); + hubRepository.save(hub); + Map body = Collections.emptyMap(); + ResponseEntity responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body); + if (responseLogin.getStatusCode() == HttpStatus.OK) { + log.info("Login successful to odessa. Parsing response."); + String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); + AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); + + // Validate and save token + if (parsedResponse.getTokenId() != null) { + hub.setAppointmentAuthTokenId(parsedResponse.getTokenId()); + hub.setAreaCode(parsedResponse.getAreaCode()); + hubRepository.save(hub); + log.info("Saved new authToken and areaCode for Hub."); + return hub; + } else { + throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again."); + } + } + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); + } + catch (FeignException.Forbidden forbiddenException) { + logForbiddenError(); + + // Extract raw response body + String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response + + // Parse JSON to check for "PasswordExpired" + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(responseBody); + JsonNode errorsNode = rootNode.path("errors"); + + if (errorsNode.isArray()) { + for (JsonNode error : errorsNode) { + // Check the main errorCode + if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + } + + // Check inside "subErrors" + JsonNode subErrorsNode = error.path("subErrors"); + if (subErrorsNode.isArray()) { + for (JsonNode subError : subErrorsNode) { + if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) { + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + } + } + } + } + } + } catch (IOException e) { + log.error("Error parsing JSON response: {}", e.getMessage()); + } + + // Regenerate the token and retry + loginToOdessa(hub, application); + } + catch (Exception e) { + log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e); + throw new RuntimeException("Authentication failed on Odessa. try again", e); + } + return null; + } private void startAsyncNdgProcessing(Long applicationId) { // Check if a thread is already running for this application @@ -259,18 +340,11 @@ public class AppointmentDao { application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); applicationRepository.save(application); companyRepository.save(company); + ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); + Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION); log.info("NDG saved successfully for applicationId: {}", application.getId()); - - // /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura" - // operation. **/ - // loggingUtil.addVersionHistory( - // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData) - // .newData(application).build()); - // - // /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/ - // loggingUtil.addVersionHistory( - // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData) - // .newData(company).build()); break; } @@ -309,10 +383,6 @@ public class AppointmentDao { private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg, String idVisura) { - //cloned for old application and company data - // ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - // CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); - application.setNdg(ndg); application.setIdVisura(idVisura); application.setNdgStatus(GepafinConstant.NDG_GENERATED); @@ -320,17 +390,10 @@ public class AppointmentDao { company.setNdg(ndg); companyRepository.save(company); applicationRepository.save(application); - Map placeHolders=notificationDao.sendNotificationToBeneficiary(application,NotificationTypeEnum.NDG_GENERATION); + ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); + Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION); notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION); - - // /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura" operation. **/ - // loggingUtil.addVersionHistory( - // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); - // - // /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/ - // loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData).newData - // (company).build()); - log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); } @@ -358,18 +421,12 @@ public class AppointmentDao { private HubEntity authenticateAndSaveToken(HubEntity hub) { - // HubEntity oldHubData = Utils.getClonedEntityForData(hub); try { //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); log.info("Got the auth for login to odessa {}", authJwtToken); hub.setAuthToken(authJwtToken); hubRepository.save(hub); - - // /** This code is responsible for adding a version history log for the "Updating auth token for login api in hub" operation. **/ - // loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData - // (hub).build()); - // Prepare the request body (adjust if necessary for login API) Map body = Collections.emptyMap(); // Perform login API call @@ -387,13 +444,6 @@ public class AppointmentDao { hub.setAreaCode(parsedResponse.getAreaCode()); hubRepository.save(hub); - // /** This code is responsible for adding a version history log for the "inserting token and areaCode from login odessa response for - // appointment flow api's" - // * operation. **/ - // loggingUtil.addVersionHistory( - // VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData(hub) - // .build()); - log.info("Saved new authToken and areaCode for Hub."); return hub; } else { @@ -404,6 +454,38 @@ public class AppointmentDao { throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); } catch (FeignException.Forbidden forbiddenException) { logForbiddenError(); + + // Extract raw response body + String responseBody = forbiddenException.contentUTF8(); // Extract raw JSON response + + // Parse JSON to check for "PasswordExpired" + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(responseBody); + JsonNode errorsNode = rootNode.path("errors"); + + if (errorsNode.isArray()) { + for (JsonNode error : errorsNode) { + // Check the main errorCode + if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { + throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + } + + // Check inside "subErrors" + JsonNode subErrorsNode = error.path("subErrors"); + if (subErrorsNode.isArray()) { + for (JsonNode subError : subErrorsNode) { + if (GepafinConstant.PASSWORD_EXPIRED.equals(subError.path("errorCode").asText())) { + throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + } + } + } + } + } + } catch (IOException e) { + log.error("Error parsing JSON response: {}", e.getMessage()); + } + // Regenerate the token and retry regenerateTokenAndSave(hub); } catch (Exception e) { @@ -435,14 +517,8 @@ public class AppointmentDao { } private String regenerateTokenAndSave(HubEntity hub) { - - try { hub = authenticateAndSaveToken(hub); return "Bearer " + hub.getAppointmentAuthTokenId(); - } catch (Exception e) { - log.error("Failed to regenerate token from Odessa: {}", e.getMessage()); - throw new RuntimeException("Token regeneration failed from Odessa.", e); - } } private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) { @@ -625,6 +701,7 @@ public class AppointmentDao { throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); } + hub = authenticateAndSaveToken(hub); // Generate authorization token and fetch template data String authorizationToken = getBearerToken(hub); Long appointmentTemplateId = application.getCall().getAppointmentTemplateId(); @@ -787,6 +864,10 @@ public class AppointmentDao { Claims claims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(request)); Long hubId = Utils.extractHubIdFromPayload(claims.getSubject()); + + // Authenticate the hub before proceeding + HubEntity hub = hubRepository.findByHubId(hubId); + authenticateAndSaveToken(hub); if (systemDoc.getDocumentAttachmentId() != null) { // If the documentAttachmentId is already set, return the response log.info("Document already uploaded with documentAttachmentId: {}", systemDoc.getDocumentAttachmentId()); @@ -825,8 +906,6 @@ public class AppointmentDao { } }); return null; - // Return an immediate response indicating the process is in progress -// throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS)); } private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index 51d4983d..2c27f379 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -396,3 +396,5 @@ validation.failed.checklist=Validation failed for checklist. error.invalid.limit=Limit should be between 1 and 3000. insufficient.score.msg = Insufficient score to pass to the technical and economic-financial evaluation + +password.expired.for.login.to.odessa = Odessa login password has been expired. diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index 8689fc2c..7b0293bf 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -387,4 +387,6 @@ validation.failed.checklist=Convalida fallita per la checklist. error.invalid.limit=Il limite dovrebbe essere compreso tra 1 e 3000. insufficient.score.msg = Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria -validation.table.message=I dati per il campo {0} non sono presenti. \ No newline at end of file +validation.table.message=I dati per il campo {0} non sono presenti. + +password.expired.for.login.to.odessa = La password di accesso a Odessa è scaduta \ No newline at end of file