diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java index e95d4ca3..b0cf463e 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java @@ -6,6 +6,11 @@ import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter; import hirs.attestationca.portal.page.PageController; import hirs.attestationca.portal.page.params.NoPageParams; import hirs.data.persist.certificate.Certificate; +import hirs.data.persist.certificate.PlatformCredential; +import hirs.data.persist.certificate.attributes.ComponentIdentifier; +import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2; +import hirs.persist.CertificateManager; +import hirs.persist.DeviceManager; import org.apache.logging.log4j.Logger; import static org.apache.logging.log4j.LogManager.getLogger; import org.hibernate.Criteria; @@ -25,6 +30,21 @@ import hirs.data.persist.SupplyChainValidationSummary; import hirs.persist.CriteriaModifier; import hirs.persist.CrudManager; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Controller for the Validation Reports page. */ @@ -33,18 +53,32 @@ import hirs.persist.CrudManager; public class ValidationReportsPageController extends PageController { private final CrudManager supplyChainValidatorSummaryManager; + private final CertificateManager certificateManager; + private final DeviceManager deviceManager; + private static String columnHeaders = "Verified Manufacturer," + + "Model,SN,Verification Date,Device Status," + + "Component name,Component manufacturer,Component model," + + "Component SN,Component status"; + private static final String DEFAULT_COMPANY = "AllDevices"; + private static final String UNDEFINED = "undefined"; private static final Logger LOGGER = getLogger(ValidationReportsPageController.class); /** * Constructor providing the Page's display and routing specification. * @param supplyChainValidatorSummaryManager the manager + * @param certificateManager the certificate manager + * @param deviceManager the device manager */ @Autowired public ValidationReportsPageController( - final CrudManager supplyChainValidatorSummaryManager) { + final CrudManager supplyChainValidatorSummaryManager, + final CertificateManager certificateManager, + final DeviceManager deviceManager) { super(VALIDATION_REPORTS); this.supplyChainValidatorSummaryManager = supplyChainValidatorSummaryManager; + this.certificateManager = certificateManager; + this.deviceManager = deviceManager; } /** @@ -97,4 +131,160 @@ public class ValidationReportsPageController extends PageController(records, input); } + + /** + * This method handles downloading a validation report. + * @param request object + * @param response object + * @throws IOException thrown by BufferedWriter object + */ + @RequestMapping(value = "download", method = RequestMethod.POST) + public void download(final HttpServletRequest request, + final HttpServletResponse response) throws IOException { + + LOGGER.info("Downloading validation report"); + String company = ""; + String contractNumber = ""; + Pattern pattern = Pattern.compile("^\\w*$"); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd"); + DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"); + LocalDate startDate = null; + LocalDate endDate = null; + ArrayList createTimes = new ArrayList(); + String[] deviceNames = new String[]{}; + + Enumeration parameters = request.getParameterNames(); + while (parameters.hasMoreElements()) { + String parameter = (String) parameters.nextElement(); + String parameterValue = request.getParameter(parameter); + LOGGER.info(parameter + ": " + parameterValue); + switch (parameter) { + case "company": + Matcher companyMatcher = pattern.matcher(parameterValue); + if (companyMatcher.matches()) { + company = parameterValue; + } else { + company = DEFAULT_COMPANY; + } + break; + case "contract": + Matcher contractMatcher = pattern.matcher(parameterValue); + if (contractMatcher.matches()) { + contractNumber = parameterValue; + } else { + contractNumber = "none"; + } + break; + case "dateStart": + if (parameterValue != null && !parameterValue.isEmpty()) { + startDate = LocalDate.parse(parameterValue, dateFormat); + } else { + startDate = LocalDate.ofEpochDay(0); + } + break; + case "dateEnd": + if (parameterValue != null && !parameterValue.isEmpty()) { + endDate = LocalDate.parse(parameterValue, dateFormat); + } else { + endDate = LocalDate.now(); + } + break; + case "createTimes": + if (!parameterValue.equals(UNDEFINED)) { + String[] timestamps = parameterValue.split(","); + for (String timestamp : timestamps) { + createTimes.add(LocalDateTime.parse(timestamp, + dateTimeFormat).toLocalDate()); + } + } + break; + case "deviceNames": + if (!parameterValue.equals(UNDEFINED)) { + deviceNames = parameterValue.split(","); + } + break; + default: + } + } + + response.setHeader("Content-Type", "text/csv"); + response.setHeader("Content-Disposition", + "attachment;filename=validation_report.csv"); + BufferedWriter bufferedWriter = new BufferedWriter( + new OutputStreamWriter(response.getOutputStream(), "UTF-8")); + StringBuilder reportData = new StringBuilder(); + bufferedWriter.append("Company: " + company + "\n"); + bufferedWriter.append("Contract number: " + contractNumber + "\n"); + for (int i = 0; i < deviceNames.length; i++) { + if ((createTimes.get(i).isAfter(startDate) || createTimes.get(i).isEqual(startDate)) + && (createTimes.get(i).isBefore(endDate) + || createTimes.get(i).isEqual(endDate))) { + UUID deviceId = deviceManager.getDevice(deviceNames[i]).getId(); + LOGGER.info(deviceId); + PlatformCredential pc = PlatformCredential.select(certificateManager) + .byDeviceId(deviceId).getCertificate(); + LOGGER.info("Found platform credential: " + pc.toString()); + reportData.append(pc.getManufacturer() + "," + + pc.getModel() + "," + + pc.getPlatformSerial() + "," + + LocalDateTime.now().toString() + "," + + pc.getDevice().getSupplyChainStatus() + ","); + ArrayList> parsedComponents = parseComponents(pc); + for (ArrayList component : parsedComponents) { + for (String data : component) { + reportData.append(data + ","); + } + reportData.deleteCharAt(reportData.length() - 1); + reportData.append("\n,,,,,"); + } + reportData.delete(reportData.lastIndexOf("\n"), reportData.length()); + } + } + bufferedWriter.append(columnHeaders + "\n"); + bufferedWriter.append(reportData.toString() + "\n"); + LOGGER.info(columnHeaders); + LOGGER.info(reportData.toString()); + bufferedWriter.flush(); + } + + /** + * This method parses the following ComponentIdentifier fields into an ArrayList of ArrayLists. + * - ComponentClass + * - Manufacturer + * - Model + * - Serial number + * - Pass/fail status (based on componentFailures string) + * @param pc the platform credential. + * @return the ArrayList of ArrayLists containing the parsed component data. + */ + private ArrayList> parseComponents(final PlatformCredential pc) { + ArrayList> parsedComponents = new ArrayList>(); + if (pc.getComponentIdentifiers() != null + && pc.getComponentIdentifiers().size() > 0) { + LOGGER.info("Component failures: " + pc.getComponentFailures()); + ArrayList componentFailures = + new ArrayList(Arrays.asList(pc.getComponentFailures().split(";"))); + for (ComponentIdentifier ci : pc.getComponentIdentifiers()) { + ArrayList componentData = new ArrayList(); + if (ci instanceof ComponentIdentifierV2) { + componentData.add(((ComponentIdentifierV2) ci).getComponentClass().toString()); + } else { + componentData.add("Platform Component"); + } + componentData.add(ci.getComponentManufacturer().getString()); + componentData.add(ci.getComponentModel().getString()); + componentData.add(ci.getComponentSerial().getString()); + //Failing components are identified by manufacturer + model + if (componentFailures.contains(componentData.get(1) + componentData.get(2))) { + componentData.add("Fail"); + } else { + componentData.add("Pass"); + } + parsedComponents.add(componentData); + LOGGER.info(String.join(",", componentData)); + } + } + + return parsedComponents; + } } diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp index 530d4f3c..a70dd6b0 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/validation-reports.jsp @@ -25,6 +25,19 @@ + + Download Validation Reports + + + +
+ + +
+
+
@@ -126,6 +139,39 @@ dataTable.order([1, 'desc']).draw(); //order by createTime }); + $("#download").submit(function(e) { + var tableLength = $("#reportTable").rows; + var createTimes = ""; + var deviceNames = ""; + $('#reportTable tr').not('thead tr').each(function() { + createTimes += $(this).find("td").eq(1).html() + ","; + deviceNames += $(this).find("td").eq(2).html() + ","; + }); + createTimes = createTimes.substring(0, createTimes.length - 1); + deviceNames = deviceNames.substring(0, deviceNames.length - 1); + var params = [ + { + name: 'createTimes', + value: createTimes + }, + { + name: 'deviceNames', + value: deviceNames + } + ]; + $(this).append($.map(params, function(param) { + return $('', { + type: 'hidden', + name: param.name, + value: param.value + }); + })); + }); + + $(".btn-primary").click(function() { + $("#validationReportsDownload").modal('hide'); + }); + /** * Gets HTML to display (icon tag) for the specified validation type. * If a validation for the requested type is not found, an empty diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/tags/download-info.tag b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/tags/download-info.tag new file mode 100644 index 00000000..f14e33e0 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/tags/download-info.tag @@ -0,0 +1,14 @@ +<%@tag description="download icon that opens modal dialog with form" pageEncoding="UTF-8"%> + +<%@attribute name="id"%> +<%@attribute name="label"%> +<%@attribute name="customButtons" fragment="true" required="false"%> + +<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %> + + + + + + + \ No newline at end of file