From 23bdd0d6a834995d2d0fa9f4fd99b57443355521 Mon Sep 17 00:00:00 2001 From: elmiomar Date: Wed, 30 Oct 2024 11:53:23 -0400 Subject: [PATCH 1/2] add support for pre-approved datasets --- ...URLConnectionRPARequestHandlerService.java | 123 +++++++++--------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/src/main/java/gov/nist/oar/distrib/service/rpa/HttpURLConnectionRPARequestHandlerService.java b/src/main/java/gov/nist/oar/distrib/service/rpa/HttpURLConnectionRPARequestHandlerService.java index 0b633d52..0add26bf 100644 --- a/src/main/java/gov/nist/oar/distrib/service/rpa/HttpURLConnectionRPARequestHandlerService.java +++ b/src/main/java/gov/nist/oar/distrib/service/rpa/HttpURLConnectionRPARequestHandlerService.java @@ -71,6 +71,7 @@ public class HttpURLConnectionRPARequestHandlerService implements RPARequestHand private final static String RECORD_PENDING_STATUS = "pending"; private final static String RECORD_APPROVED_STATUS = "approved"; + private final static String RECORD_PRE_APPROVED_STATUS = "pre-approved"; private final static String RECORD_DECLINED_STATUS = "declined"; private final static String RECORD_REJECTED_STATUS = "rejected"; @@ -269,21 +270,19 @@ public RecordWrapper getRecord(String recordId) throws RecordNotFoundException, * @throws InvalidRequestException If the request is invalid or incomplete. * @throws RequestProcessingException If an error occurs during request processing, including URL creation, serialization, or communication errors. */ - @Override - public RecordWrapper createRecord(UserInfoWrapper userInfoWrapper) throws InvalidRequestException, - RequestProcessingException { + public RecordWrapper createRecord(UserInfoWrapper userInfoWrapper) throws InvalidRequestException, RequestProcessingException { int responseCode; - + RecordWrapper newRecordWrapper; + // Validate the email and country against the blacklists before proceeding String email = userInfoWrapper.getUserInfo().getEmail(); String country = userInfoWrapper.getUserInfo().getCountry(); String rejectionReason = ""; - - // Log user info + LOGGER.debug("User's email: " + email); LOGGER.debug("User's country: " + country); - + // Check for blacklisted email and country if (isEmailBlacklisted(email)) { rejectionReason = "Email " + email + " is blacklisted."; @@ -292,29 +291,23 @@ public RecordWrapper createRecord(UserInfoWrapper userInfoWrapper) throws Invali rejectionReason = "Country " + country + " is blacklisted."; LOGGER.warn("Country {} is blacklisted. Request to create record will be automatically rejected.", country); } - - // Set the pending status of the record in this service, and not based on the user's input - userInfoWrapper.getUserInfo().setApprovalStatus(RECORD_PENDING_STATUS); - + + // Set the appropriate approval status based on blacklisting if (!rejectionReason.isEmpty()) { - // Append the rejection reason to the existing description - String currentDescription = userInfoWrapper.getUserInfo().getDescription(); - String updatedDescription = currentDescription + "\nThis record was automatically rejected. Reason: " + rejectionReason; - userInfoWrapper.getUserInfo().setDescription(updatedDescription); - // Set approval status to rejected userInfoWrapper.getUserInfo().setApprovalStatus(RECORD_REJECTED_STATUS); + String updatedDescription = userInfoWrapper.getUserInfo().getDescription() + + "\nThis record was automatically rejected. Reason: " + rejectionReason; + userInfoWrapper.getUserInfo().setDescription(updatedDescription); + } else { + // Set pending or pre-approved status based on dataset type + String datasetId = userInfoWrapper.getUserInfo().getSubject(); + boolean isPreApproved = isPreApprovedDataset(datasetId); + userInfoWrapper.getUserInfo().setApprovalStatus(isPreApproved ? RECORD_PRE_APPROVED_STATUS : RECORD_PENDING_STATUS); } - - // Initialize return value - RecordWrapper newRecordWrapper; - - // Get path + + // Send the POST request to Salesforce String createRecordUri = getConfig().getSalesforceEndpoints().get(CREATE_RECORD_ENDPOINT_KEY); - - // Get token JWTToken token = jwtHelper.getToken(); - - // Build URL String url; try { url = new URIBuilder(token.getInstanceUrl()) @@ -325,89 +318,91 @@ public RecordWrapper createRecord(UserInfoWrapper userInfoWrapper) throws Invali throw new RequestProcessingException("Error building URI: " + e.getMessage()); } LOGGER.debug("CREATE_RECORD_URL=" + url); - + + // Serialize the request payload String postPayload; try { postPayload = prepareRequestPayload(userInfoWrapper); - // Log the payload before serialization LOGGER.debug("CREATE_RECORD_PAYLOAD=" + postPayload); } catch (JsonProcessingException e) { LOGGER.error("Error while preparing record creation payload: " + e.getMessage()); throw new RequestProcessingException("Error while preparing record creation payload: " + e.getMessage()); } - - // Send POST request + + // Send the POST request HttpURLConnection connection = null; - + try { URL requestUrl = new URL(url); connection = connectionFactory.createHttpURLConnection(requestUrl); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("Authorization", "Bearer " + token.getAccessToken()); - // Set payload - byte[] payloadBytes = postPayload.getBytes(StandardCharsets.UTF_8); - connection.setDoOutput(true); // tell connection we are writing data to the output stream - OutputStream os = connection.getOutputStream(); - os.write(payloadBytes); - os.flush(); - os.close(); - + connection.setDoOutput(true); + + // Write payload to output stream + try (OutputStream os = connection.getOutputStream()) { + os.write(postPayload.getBytes(StandardCharsets.UTF_8)); + os.flush(); + } + + // Process Salesforce response responseCode = connection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { // If created + if (responseCode == HttpURLConnection.HTTP_OK) { try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { StringBuilder response = new StringBuilder(); String line; - while ((line = in.readLine()) != null) { response.append(line); } LOGGER.debug("CREATE_RECORD_RESPONSE=" + response); - // Handle the response newRecordWrapper = new ObjectMapper().readValue(response.toString(), RecordWrapper.class); } } else { - // Handle any other error response LOGGER.debug("Error response from Salesforce service: " + connection.getResponseMessage()); throw new RequestProcessingException("Error response from Salesforce service: " + connection.getResponseMessage()); } - - } catch (MalformedURLException e) { - // Handle the URL Malformed error - LOGGER.debug("Invalid URL: " + e.getMessage()); - throw new RequestProcessingException("Invalid URL: " + e.getMessage()); } catch (IOException e) { - // Handle the I/O error - LOGGER.debug("Error sending GET request: " + e.getMessage()); + LOGGER.debug("Error sending request: " + e.getMessage()); throw new RequestProcessingException("I/O error: " + e.getMessage()); } finally { - // Close the connection if (connection != null) { connection.disconnect(); } } - - // Check if success and handle accordingly + + // Handle record creation based on the dataset type and approval status if (newRecordWrapper != null) { - // Check if the record is marked as rejected before proceeding - if (!RECORD_REJECTED_STATUS.equals(newRecordWrapper.getRecord().getUserInfo().getApprovalStatus())) { - // If the record is not marked as rejected, proceed with the normal success handling, - // including sending emails for SME approval and to the requester. - this.recordResponseHandler.onRecordCreationSuccess(newRecordWrapper.getRecord()); + String approvalStatus = newRecordWrapper.getRecord().getUserInfo().getApprovalStatus(); + + if (RECORD_REJECTED_STATUS.equals(approvalStatus)) { + LOGGER.info("Record automatically rejected due to blacklist. Skipping further processing."); + } else if (RECORD_PRE_APPROVED_STATUS.equals(approvalStatus)) { + // Handle pre-approved dataset: cache immediately and send both confirmation and download emails + String randomId = this.rpaDatasetCacher.cache(userInfoWrapper.getUserInfo().getSubject()); + if (randomId == null) { + throw new RequestProcessingException("Caching process returned a null randomId"); + } + this.recordResponseHandler.onPreApprovedRecordCreationSuccess(newRecordWrapper.getRecord(), randomId); } else { - // Since the record is automatically rejected, we skip sending approval and notification emails. - LOGGER.info("Record automatically rejected due to blacklist. Skipping email notifications."); + // Handle SME-required dataset: send SME approval request and confirmation email + this.recordResponseHandler.onRecordCreationSuccess(newRecordWrapper.getRecord()); } } else { - // we expect a record to be created every time we call createRecord - // if newRecordWrapper is null, it means creation failed + // Handle failure if record creation did not succeed this.recordResponseHandler.onRecordCreationFailure(responseCode); } - - + return newRecordWrapper; } + + private boolean isPreApprovedDataset(String datasetId) { + List approvers = rpaConfiguration.getApprovers().get(datasetId); + return approvers == null || approvers.isEmpty(); + } + + private boolean isEmailBlacklisted(String email) { List disallowedEmailStrings = rpaConfiguration.getDisallowedEmails(); for (String patternString : disallowedEmailStrings) { @@ -432,7 +427,7 @@ private String prepareRequestPayload(UserInfoWrapper userInfoWrapper) throws Jso // Serialize the userInfoWrapper object to JSON return new ObjectMapper().writeValueAsString(userInfoWrapper); } - + /** * Updates the status of a record in the database. From 08fa4e22a311852cbe93c49c7408d13ee01469e1 Mon Sep 17 00:00:00 2001 From: elmiomar Date: Wed, 30 Oct 2024 11:53:59 -0400 Subject: [PATCH 2/2] add a new handler for pre approved records --- .../service/rpa/RecordResponseHandler.java | 7 ++++ .../rpa/RecordResponseHandlerImpl.java | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandler.java b/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandler.java index 7e29f588..f47c9ba0 100644 --- a/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandler.java +++ b/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandler.java @@ -19,6 +19,13 @@ public interface RecordResponseHandler { */ void onRecordCreationFailure(int statusCode); + /** + * Called when a pre-approved record creation operation succeeds. + * @param record The newly created pre-approved record + * @param randomId The unique identifier for the cached dataset associated with the record + */ + void onPreApprovedRecordCreationSuccess(Record record, String randomId); + /** * This method is called when a record update operation is successful and user was approved. * @param record The record that was the user approved for. diff --git a/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandlerImpl.java b/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandlerImpl.java index ca7f4150..66df58b4 100644 --- a/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandlerImpl.java +++ b/src/main/java/gov/nist/oar/distrib/service/rpa/RecordResponseHandlerImpl.java @@ -9,6 +9,7 @@ import gov.nist.oar.distrib.service.rpa.model.EmailInfoWrapper; import gov.nist.oar.distrib.service.rpa.model.JWTToken; import gov.nist.oar.distrib.service.rpa.model.Record; +import gov.nist.oar.distrib.service.rpa.model.UserInfo; import gov.nist.oar.distrib.web.RPAConfiguration; import org.apache.http.client.utils.URIBuilder; import org.slf4j.Logger; @@ -90,6 +91,43 @@ public void onRecordCreationFailure(int statusCode) throws RequestProcessingExce LOGGER.debug("Failed to create record, status_code=" + statusCode); throw new RequestProcessingException("Failed to create record, status_code=" + statusCode); } + + /** + * Called when a pre-approved record creation operation succeeds. + * Handles caching and sends both confirmation and download emails immediately. + * @param record The newly created pre-approved record + * @param randomId The unique identifier for the cached dataset associated with the record + */ + @Override + public void onPreApprovedRecordCreationSuccess(Record record, String randomId) throws RequestProcessingException { + LOGGER.debug("Handling success for pre-approved record with ID: " + record.getId()); + + // Send confirmation email to the user + if (this.emailSender.sendConfirmationEmailToEndUser(record)) { + LOGGER.debug("Confirmation email sent successfully for pre-approved record (RecordID=" + record.getId() + ")"); + } else { + throw new RequestProcessingException("Failed to send confirmation email for pre-approved record"); + } + + // Generate the download URL + String downloadUrl; + try { + downloadUrl = new URIBuilder(this.rpaConfiguration.getDatacartUrl()) + .setParameter("id", randomId) + .build() + .toString(); + } catch (URISyntaxException e) { + throw new RequestProcessingException("Error building URI: " + e.getMessage()); + } + + // Send download email to the user + if (this.emailSender.sendDownloadEmailToEndUser(record, downloadUrl)) { + LOGGER.debug("Download email sent successfully for pre-approved record (RecordID=" + record.getId() + ")"); + } else { + throw new RequestProcessingException("Failed to send download email for pre-approved record"); + } + } + /** * Called when a record update operation has been approved. @@ -320,4 +358,5 @@ private int send(EmailInfo emailInfo) throws InvalidRequestException, RequestPro return responseCode; } } + }