Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Pre-Approved Datasets #116

Open
wants to merge 2 commits into
base: integration
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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.";
Expand All @@ -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())
Expand All @@ -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<RPAConfiguration.Approver.ApproverData> approvers = rpaConfiguration.getApprovers().get(datasetId);
return approvers == null || approvers.isEmpty();
}


private boolean isEmailBlacklisted(String email) {
List<String> disallowedEmailStrings = rpaConfiguration.getDisallowedEmails();
for (String patternString : disallowedEmailStrings) {
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -320,4 +358,5 @@ private int send(EmailInfo emailInfo) throws InvalidRequestException, RequestPro
return responseCode;
}
}

}
Loading