From 44860366ad15514e76e814d7853010947100f436 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Tue, 8 Apr 2025 18:00:29 -0400 Subject: [PATCH] v3_issue_811: can search on all pages. currently placing all the cert pages into their own controller class due to the massive size of the main cert page controller class and because the changes i want to implement require it. --- .../PlatformCertificateRepository.java | 5 +- .../persist/service/CertificateService.java | 312 ++++++++++- .../CertificatePageController.java | 518 +++++------------- .../EndorsementCredentialPageController.java | 397 ++++++++++++++ .../IDevIdCertificatePageController.java | 119 ++++ .../IssuedCertificateController.java | 241 ++++++++ .../PlatformCredentialPageController.java | 417 ++++++++++++++ .../TrustChainCertificatePageController.java | 286 ++++++++++ .../src/main/webapp/WEB-INF/jsp/devices.jsp | 250 +++++---- .../jsp/endorsement-key-credentials.jsp | 37 +- .../WEB-INF/jsp/idevid-certificates.jsp | 166 +++--- .../WEB-INF/jsp/issued-certificates.jsp | 2 +- .../WEB-INF/jsp/platform-credentials.jsp | 2 +- .../WEB-INF/jsp/reference-manifests.jsp | 156 +++--- .../main/webapp/WEB-INF/jsp/rim-database.jsp | 144 ++--- .../main/webapp/WEB-INF/jsp/trust-chain.jsp | 136 ++--- 16 files changed, 2398 insertions(+), 790 deletions(-) create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/EndorsementCredentialPageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IDevIdCertificatePageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IssuedCertificateController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PlatformCredentialPageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/TrustChainCertificatePageController.java diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/PlatformCertificateRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/PlatformCertificateRepository.java index ff2af12c..ad7a4190 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/PlatformCertificateRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/PlatformCertificateRepository.java @@ -4,15 +4,13 @@ import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredent import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; import java.util.List; import java.util.UUID; @Repository -public interface PlatformCertificateRepository extends JpaRepository, - JpaSpecificationExecutor { +public interface PlatformCertificateRepository extends JpaRepository { /** * Query that retrieves a list of platform credentials using the provided archive flag. @@ -32,7 +30,6 @@ public interface PlatformCertificateRepository extends JpaRepository findByArchiveFlag(boolean archiveFlag, Pageable pageable); - /** * Query that retrieves a list of platform credentials using the provided device id. * diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateService.java index 74aeee29..f3a572c6 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateService.java @@ -1,11 +1,20 @@ package hirs.attestationca.persist.service; +import hirs.attestationca.persist.DBServiceException; +import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.ComponentResultRepository; +import hirs.attestationca.persist.entity.userdefined.Certificate; +import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult; +import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -13,16 +22,34 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.UUID; +/** + * Service layer class that handles the storage and retrieval of all types of certificates. + */ +@Log4j2 @Service public class CertificateService { + private static final String TRUSTCHAIN = "trust-chain"; + private static final String PLATFORMCREDENTIAL = "platform-credentials"; + private static final String IDEVIDCERTIFICATE = "idevid-certificates"; + private static final String ENDORSEMENTCREDENTIAL = "endorsement-key-credentials"; + private static final String ISSUEDCERTIFICATES = "issued-certificates"; + + private final CertificateRepository certificateRepository; + private final ComponentResultRepository componentResultRepository; private final EntityManager entityManager; @Autowired - public CertificateService(EntityManager entityManager) { + public CertificateService(final CertificateRepository certificateRepository, + final ComponentResultRepository componentResultRepository, + final EntityManager entityManager) { + this.certificateRepository = certificateRepository; + this.componentResultRepository = componentResultRepository; this.entityManager = entityManager; } @@ -30,10 +57,10 @@ public class CertificateService { * @param entityClass generic entity class * @param searchableColumns list of the searchable column name * @param searchText text that waas input in the search textbox - * @param archiveFlag - * @param pageable + * @param archiveFlag archive flag + * @param pageable pageable * @param generic entity class - * @return + * @return page full of the generic certificates. */ public Page findBySearchableColumnsAndArchiveFlag(Class entityClass, List searchableColumns, @@ -48,7 +75,7 @@ public class CertificateService { // Dynamically add search conditions for each field that should be searchable if (!StringUtils.isBlank(searchText)) { - // Dynamically loop through columns and create LIKE conditions for each one + // Dynamically loop through columns and create LIKE conditions for each searchable column for (String columnName : searchableColumns) { Predicate predicate = criteriaBuilder.like(criteriaBuilder.lower(certificate.get(columnName)), @@ -73,4 +100,279 @@ public class CertificateService { List resultList = typedQuery.getResultList(); return new PageImpl<>(resultList, pageable, totalRows); } + + /** + * Store the given certificate in the database. + * + * @param certificateType String containing the certificate type + * @param fileName contain the name of the file of the certificate to + * be stored + * @param successMessages contains any messages that will be display on the page + * @param certificate the certificate to store + */ + public void storeCertificate( + final String certificateType, + final String fileName, + final List successMessages, + final List errorMessages, + final Certificate certificate) { + + Certificate existingCertificate; + + // look for an identical certificate in the database + try { + existingCertificate = getCertificateByHash( + certificateType, + certificate.getCertificateHash()); + } catch (DBServiceException dbsEx) { + final String failMessage = "Querying for existing certificate failed (" + + fileName + "): "; + errorMessages.add(failMessage + dbsEx.getMessage()); + log.error(failMessage, dbsEx); + return; + } + + try { + // save the new certificate if no match is found + if (existingCertificate == null) { + if (certificateType.equals(PLATFORMCREDENTIAL)) { + PlatformCredential platformCertificate = (PlatformCredential) certificate; + if (platformCertificate.isPlatformBase()) { + List sharedCertificates = getPlatformCertificateByBoardSN( + platformCertificate.getPlatformSerial()); + for (PlatformCredential pc : sharedCertificates) { + if (pc.isPlatformBase()) { + final String failMessage = "Storing certificate failed: " + + "platform credential " + + "chain (" + pc.getPlatformSerial() + + ") base already exists in this chain (" + + fileName + ")"; + errorMessages.add(failMessage); + log.error(failMessage); + return; + } + } + } + } + + this.certificateRepository.save(certificate); + parseAndSaveComponentResults(certificate); + + final String successMsg + = String.format("New certificate successfully uploaded (%s): ", fileName); + successMessages.add(successMsg); + log.info(successMsg); + return; + } + } catch (DBServiceException dbsEx) { + final String failMessage = String.format("Storing new certificate failed (%s): ", + fileName); + errorMessages.add(failMessage + dbsEx.getMessage()); + log.error(failMessage, dbsEx); + return; + } catch (IOException ioException) { + final String ioExceptionMessage = "Failed to save component results in the database"; + errorMessages.add(ioExceptionMessage + ioException.getMessage()); + log.error(ioExceptionMessage, ioException); + return; + } + + try { + // if an identical certificate is archived, update the existing certificate to + // unarchive it and change the creation date + if (existingCertificate.isArchived()) { + existingCertificate.restore(); + existingCertificate.resetCreateTime(); + this.certificateRepository.save(existingCertificate); + + List componentResults = componentResultRepository + .findByBoardSerialNumber(((PlatformCredential) existingCertificate) + .getPlatformSerial()); + for (ComponentResult componentResult : componentResults) { + componentResult.restore(); + componentResult.resetCreateTime(); + this.componentResultRepository.save(componentResult); + } + + final String successMsg = String.format("Pre-existing certificate " + + "found and unarchived (%s): ", fileName); + successMessages.add(successMsg); + log.info(successMsg); + return; + } + } catch (DBServiceException dbsEx) { + final String failMessage = String.format("Found an identical" + + " pre-existing certificate in the " + + "archive, but failed to unarchive it (%s): ", fileName); + errorMessages.add(failMessage + dbsEx.getMessage()); + log.error(failMessage, dbsEx); + return; + } + + // if an identical certificate is already unarchived, do nothing and show a fail message + final String failMessage + = String.format("Storing certificate failed: an identical" + + " certificate already exists (%s): ", fileName); + errorMessages.add(failMessage); + log.error(failMessage); + } + + public void deleteCertificate(UUID uuid, String certificateType, + final List successMessages, + final List errorMessages) { + + Certificate certificate = certificateRepository.getCertificate(uuid); + + if (certificate == null) { + // Use the term "record" here to avoid user confusion b/t cert and cred + String notFoundMessage = "Unable to locate record with ID: " + uuid; + errorMessages.add(notFoundMessage); + log.warn(notFoundMessage); + } else { + if (certificateType.equals(PLATFORMCREDENTIAL)) { + PlatformCredential platformCertificate = (PlatformCredential) certificate; + if (platformCertificate.isPlatformBase()) { + // only do this if the base is being deleted. + List sharedCertificates = getPlatformCertificateByBoardSN( + platformCertificate.getPlatformSerial()); + + for (PlatformCredential pc : sharedCertificates) { + if (!pc.isPlatformBase()) { + pc.archive("User requested deletion via UI of the base certificate"); + certificateRepository.save(pc); + deleteComponentResults(pc.getPlatformSerial()); + } + } + } + deleteComponentResults(platformCertificate.getPlatformSerial()); + } + + certificate.archive("User requested deletion via UI"); + certificateRepository.save(certificate); + + String deleteCompletedMessage = "Certificate successfully deleted"; + successMessages.add(deleteCompletedMessage); + log.info(deleteCompletedMessage); + } + } + + /** + * Gets the certificate by the hash code of its bytes. Looks for both + * archived and unarchived certificates. + * + * @param certificateType String containing the certificate type + * @param certificateHash the hash of the certificate's bytes + * @return the certificate or null if none is found + */ + private Certificate getCertificateByHash( + final String certificateType, + final int certificateHash) { + return switch (certificateType) { + case PLATFORMCREDENTIAL -> this.certificateRepository + .findByCertificateHash(certificateHash, + "PlatformCredential"); + case ENDORSEMENTCREDENTIAL -> this.certificateRepository + .findByCertificateHash(certificateHash, + "EndorsementCredential"); + case TRUSTCHAIN -> this.certificateRepository + .findByCertificateHash(certificateHash, + "CertificateAuthorityCredential"); + case IDEVIDCERTIFICATE -> this.certificateRepository + .findByCertificateHash(certificateHash, + "IDevIDCertificate"); + default -> null; + }; + } + + /** + * Gets the certificate by the platform serial number. + * + * @param serialNumber the platform serial number + * @return the certificate or null if none is found + */ + private List getPlatformCertificateByBoardSN( + final String serialNumber) { + List associatedCertificates = new ArrayList<>(); + + if (serialNumber != null) { + associatedCertificates.addAll(this.certificateRepository.byBoardSerialNumber(serialNumber)); + } + + return associatedCertificates; + } + + /** + * Helper method that utilizes the components of the provided platform certificate to generate + * a collection of component results and subsequently stores these results in the database. + * + * @param certificate certificate + */ + private void parseAndSaveComponentResults(final Certificate certificate) throws IOException { + PlatformCredential platformCredential; + + if (certificate instanceof PlatformCredential) { + platformCredential = (PlatformCredential) certificate; + List componentResults = componentResultRepository + .findByCertificateSerialNumberAndBoardSerialNumber( + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformSerial()); + + if (componentResults.isEmpty()) { + ComponentResult componentResult; + + if (platformCredential.getPlatformConfigurationV1() != null) { + + List componentIdentifiers = + platformCredential.getComponentIdentifiers(); + + for (ComponentIdentifier componentIdentifier : componentIdentifiers) { + componentResult = new ComponentResult(platformCredential.getPlatformSerial(), + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformChainType(), + componentIdentifier); + componentResult.setFailedValidation(false); + componentResult.setDelta(!platformCredential.isPlatformBase()); + componentResultRepository.save(componentResult); + } + } else if (platformCredential.getPlatformConfigurationV2() != null) { + + List componentIdentifiersV2 = + platformCredential.getComponentIdentifiersV2(); + + for (ComponentIdentifierV2 componentIdentifierV2 : componentIdentifiersV2) { + componentResult = new ComponentResult(platformCredential.getPlatformSerial(), + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformChainType(), + componentIdentifierV2); + componentResult.setFailedValidation(false); + componentResult.setDelta(!platformCredential.isPlatformBase()); + componentResultRepository.save(componentResult); + } + } + } else { + for (ComponentResult componentResult : componentResults) { + componentResult.restore(); + componentResult.resetCreateTime(); + componentResultRepository.save(componentResult); + } + } + } + } + + + /** + * Helper method that deletes component results based on the provided platform serial number. + * + * @param platformSerial platform serial number + */ + private void deleteComponentResults(final String platformSerial) { + List componentResults = componentResultRepository + .findByBoardSerialNumber(platformSerial); + + for (ComponentResult componentResult : componentResults) { + componentResult.archive(); + componentResultRepository.save(componentResult); + } + } + } diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java index 5787a896..4a81f1ed 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java @@ -6,19 +6,14 @@ import hirs.attestationca.persist.FilteredRecordsList; import hirs.attestationca.persist.entity.manager.CACredentialRepository; import hirs.attestationca.persist.entity.manager.CertificateRepository; import hirs.attestationca.persist.entity.manager.ComponentResultRepository; -import hirs.attestationca.persist.entity.manager.EndorsementCredentialRepository; import hirs.attestationca.persist.entity.manager.IDevIDCertificateRepository; import hirs.attestationca.persist.entity.manager.IssuedCertificateRepository; -import hirs.attestationca.persist.entity.manager.PlatformCertificateRepository; import hirs.attestationca.persist.entity.userdefined.Certificate; import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult; -import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; import hirs.attestationca.persist.entity.userdefined.certificate.IDevIDCertificate; import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate; import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; -import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; -import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; import hirs.attestationca.persist.service.CertificateService; import hirs.attestationca.persist.util.CredentialHelper; import hirs.attestationca.portal.datatables.Column; @@ -41,6 +36,7 @@ import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StreamUtils; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -62,7 +58,6 @@ import java.security.cert.X509Certificate; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; @@ -84,14 +79,10 @@ public class CertificatePageController extends PageController { */ static final String ACA_CERT_DATA = "acaCertData"; private static final String TRUSTCHAIN = "trust-chain"; - private static final String PLATFORMCREDENTIAL = "platform-credentials"; private static final String IDEVIDCERTIFICATE = "idevid-certificates"; - private static final String ENDORSEMENTCREDENTIAL = "endorsement-key-credentials"; private static final String ISSUEDCERTIFICATES = "issued-certificates"; private final CertificateRepository certificateRepository; - private final PlatformCertificateRepository platformCertificateRepository; private final ComponentResultRepository componentResultRepository; - private final EndorsementCredentialRepository endorsementCredentialRepository; private final IssuedCertificateRepository issuedCertificateRepository; private final CACredentialRepository caCredentialRepository; private final IDevIDCertificateRepository iDevIDCertificateRepository; @@ -101,20 +92,16 @@ public class CertificatePageController extends PageController { /** * Constructor providing the Page's display and routing specification. * - * @param certificateRepository the general certificate manager - * @param platformCertificateRepository the platform credential manager - * @param componentResultRepository the component result repo - * @param endorsementCredentialRepository the endorsement credential manager - * @param issuedCertificateRepository the issued certificate manager - * @param caCredentialRepository the ca credential manager - * @param iDevIDCertificateRepository the IDevID certificate repository - * @param acaCertificate the ACA's X509 certificate + * @param certificateRepository the general certificate manager + * @param componentResultRepository the component result repo + * @param issuedCertificateRepository the issued certificate manager + * @param caCredentialRepository the ca credential manager + * @param iDevIDCertificateRepository the IDevID certificate repository + * @param acaCertificate the ACA's X509 certificate */ @Autowired public CertificatePageController(final CertificateRepository certificateRepository, - final PlatformCertificateRepository platformCertificateRepository, final ComponentResultRepository componentResultRepository, - final EndorsementCredentialRepository endorsementCredentialRepository, final IssuedCertificateRepository issuedCertificateRepository, final CACredentialRepository caCredentialRepository, final IDevIDCertificateRepository iDevIDCertificateRepository, @@ -122,9 +109,7 @@ public class CertificatePageController extends PageController { final X509Certificate acaCertificate) { super(Page.TRUST_CHAIN); this.certificateRepository = certificateRepository; - this.platformCertificateRepository = platformCertificateRepository; this.componentResultRepository = componentResultRepository; - this.endorsementCredentialRepository = endorsementCredentialRepository; this.issuedCertificateRepository = issuedCertificateRepository; this.caCredentialRepository = caCredentialRepository; this.iDevIDCertificateRepository = iDevIDCertificateRepository; @@ -149,8 +134,6 @@ public class CertificatePageController extends PageController { private static Page getCertificatePage(final String certificateType) { // get page information (default to TRUST_CHAIN) return switch (certificateType) { - case PLATFORMCREDENTIAL -> Page.PLATFORM_CREDENTIALS; - case ENDORSEMENTCREDENTIAL -> Page.ENDORSEMENT_KEY_CREDENTIALS; case ISSUEDCERTIFICATES -> Page.ISSUED_CERTIFICATES; case IDEVIDCERTIFICATE -> Page.IDEVID_CERTIFICATES; default -> Page.TRUST_CHAIN; @@ -165,8 +148,6 @@ public class CertificatePageController extends PageController { */ private static Class getCertificateClass(final String certificateType) { return switch (certificateType) { - case PLATFORMCREDENTIAL -> PlatformCredential.class; - case ENDORSEMENTCREDENTIAL -> EndorsementCredential.class; case ISSUEDCERTIFICATES -> IssuedAttestationCertificate.class; case IDEVIDCERTIFICATE -> IDevIDCertificate.class; case TRUSTCHAIN -> CertificateAuthorityCredential.class; @@ -206,15 +187,9 @@ public class CertificatePageController extends PageController { HashMap data = new HashMap<>(); // add page information switch (certificateType) { - case PLATFORMCREDENTIAL: - mav = getBaseModelAndView(Page.PLATFORM_CREDENTIALS); - break; case IDEVIDCERTIFICATE: mav = getBaseModelAndView(Page.IDEVID_CERTIFICATES); break; - case ENDORSEMENTCREDENTIAL: - mav = getBaseModelAndView(Page.ENDORSEMENT_KEY_CREDENTIALS); - break; case ISSUEDCERTIFICATES: mav = getBaseModelAndView(Page.ISSUED_CERTIFICATES); break; @@ -241,26 +216,15 @@ public class CertificatePageController extends PageController { */ private List findSearchableColumnsNames(List columns) { - // grab all the columns that are searchable, then grab all of those columns names and - // create a list of those string names + // Retrieve all searchable columns and collect their names into a list of strings. return columns.stream().filter(Column::isSearchable).map(Column::getName) .collect(Collectors.toList()); } - /** - * Queries for the list of Certificates and returns a data table response - * with the records. - * - * @param certificateType String containing the certificate type - * @param input the DataTables search/query parameters - * @return the data table - */ @ResponseBody - @RequestMapping(value = "/{certificateType}/list", - produces = MediaType.APPLICATION_JSON_VALUE, - method = RequestMethod.GET) - public DataTableResponse getTableData( - @PathVariable("certificateType") final String certificateType, + @GetMapping(value = "/trust-chain/list", + produces = MediaType.APPLICATION_JSON_VALUE) + public DataTableResponse getTrustChainTableData( final DataTableInput input) { log.debug("Handling list request: {}", input); @@ -275,178 +239,132 @@ public class CertificatePageController extends PageController { int currentPage = input.getStart() / input.getLength(); Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); - // special parsing for platform credential - // Add the EndorsementCredential for each PlatformCredential based on the - // serial number. (pc.HolderSerialNumber = ec.SerialNumber) - switch (certificateType) { - case PLATFORMCREDENTIAL -> { - FilteredRecordsList records = new FilteredRecordsList<>(); + FilteredRecordsList records = new FilteredRecordsList<>(); - org.springframework.data.domain.Page pagedResult; + org.springframework.data.domain.Page pagedResult; - if (StringUtils.isBlank(searchText)) { - pagedResult = - this.platformCertificateRepository.findByArchiveFlag(false, pageable); - } else { - pagedResult = - this.certificateService.findBySearchableColumnsAndArchiveFlag( - PlatformCredential.class, - searchableColumns, - searchText, - false, pageable); - } - - if (pagedResult.hasContent()) { - records.addAll(pagedResult.getContent()); - records.setRecordsTotal(pagedResult.getContent().size()); - } else { - records.setRecordsTotal(input.getLength()); - } - - records.setRecordsFiltered(platformCertificateRepository.findByArchiveFlag(false).size()); - EndorsementCredential associatedEC; - - if (!records.isEmpty()) { - // loop all the platform certificates - for (PlatformCredential pc : records) { - // find the EC using the PC's "holder serial number" - associatedEC = this.endorsementCredentialRepository - .findBySerialNumber(pc.getHolderSerialNumber()); - - if (associatedEC != null) { - log.debug("EC ID for holder s/n {} = {}", pc - .getHolderSerialNumber(), associatedEC.getId()); - } - - pc.setEndorsementCredential(associatedEC); - } - } - - log.debug("Returning the size of the list of platform credentials: {}", records.size()); - return new DataTableResponse<>(records, input); - } - case ENDORSEMENTCREDENTIAL -> { - FilteredRecordsList records = new FilteredRecordsList<>(); - - org.springframework.data.domain.Page pagedResult; - - if (StringUtils.isBlank(searchText)) { - pagedResult = this.endorsementCredentialRepository.findByArchiveFlag(false, pageable); - } else { - pagedResult = - this.certificateService.findBySearchableColumnsAndArchiveFlag( - EndorsementCredential.class, - searchableColumns, - searchText, - false, pageable); - } - - if (pagedResult.hasContent()) { - records.addAll(pagedResult.getContent()); - records.setRecordsTotal(pagedResult.getContent().size()); - } else { - records.setRecordsTotal(input.getLength()); - } - - records.setRecordsFiltered(endorsementCredentialRepository.findByArchiveFlag(false).size()); - - log.debug("Returning the size of the list of endorsement credentials: {}", records.size()); - return new DataTableResponse<>(records, input); - } - case TRUSTCHAIN -> { - FilteredRecordsList records = new FilteredRecordsList<>(); - - org.springframework.data.domain.Page pagedResult; - - if (StringUtils.isBlank(searchText)) { - pagedResult = - this.caCredentialRepository.findByArchiveFlag(false, pageable); - } else { - pagedResult = - this.certificateService.findBySearchableColumnsAndArchiveFlag( - CertificateAuthorityCredential.class, - searchableColumns, - searchText, - false, pageable); - } - - - if (pagedResult.hasContent()) { - records.addAll(pagedResult.getContent()); - records.setRecordsTotal(pagedResult.getContent().size()); - } else { - records.setRecordsTotal(input.getLength()); - } - - records.setRecordsFiltered(caCredentialRepository.findByArchiveFlag(false).size()); - - log.debug("Returning the size of the list of trust chain certificates: {}", records.size()); - return new DataTableResponse<>(records, input); - } - case ISSUEDCERTIFICATES -> { - FilteredRecordsList records = new FilteredRecordsList<>(); - org.springframework.data.domain.Page pagedResult; - - if (StringUtils.isBlank(searchText)) { - pagedResult = - this.issuedCertificateRepository.findByArchiveFlag(false, pageable); - } else { - pagedResult = - this.certificateService.findBySearchableColumnsAndArchiveFlag( - IssuedAttestationCertificate.class, - searchableColumns, - searchText, - false, pageable); - } - - if (pagedResult.hasContent()) { - records.addAll(pagedResult.getContent()); - records.setRecordsTotal(pagedResult.getContent().size()); - } else { - records.setRecordsTotal(input.getLength()); - } - - records.setRecordsFiltered(issuedCertificateRepository.findByArchiveFlag(false).size()); - - log.debug("Returning the size of the list of issued certificates: {}", records.size()); - return new DataTableResponse<>(records, input); - } - case IDEVIDCERTIFICATE -> { - FilteredRecordsList records = new FilteredRecordsList<>(); - org.springframework.data.domain.Page pagedResult; - - if (StringUtils.isBlank(searchText)) { - pagedResult = - this.iDevIDCertificateRepository.findByArchiveFlag(false, pageable); - } else { - pagedResult = - this.certificateService.findBySearchableColumnsAndArchiveFlag( - IDevIDCertificate.class, - searchableColumns, - searchText, - false, pageable); - } - - if (pagedResult.hasContent()) { - records.addAll(pagedResult.getContent()); - records.setRecordsTotal(pagedResult.getContent().size()); - } else { - records.setRecordsTotal(input.getLength()); - } - - records.setRecordsFiltered(iDevIDCertificateRepository.findByArchiveFlag(false).size()); - - log.debug("Returning the size of the list of IDEVID certificates: {}", records.size()); - return new DataTableResponse<>(records, input); - } - default -> { - log.error("Cannot provide the size of the records because the" - + "provided certificate type does not exist."); - return new DataTableResponse<>(new FilteredRecordsList<>(), input); - } + if (StringUtils.isBlank(searchText)) { + pagedResult = + this.caCredentialRepository.findByArchiveFlag(false, pageable); + } else { + pagedResult = + this.certificateService.findBySearchableColumnsAndArchiveFlag( + CertificateAuthorityCredential.class, + searchableColumns, + searchText, + false, pageable); } + + + if (pagedResult.hasContent()) { + records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); + } + + records.setRecordsFiltered(caCredentialRepository.findByArchiveFlag(false).size()); + + log.debug("Returning the size of the list of trust chain certificates: {}", records.size()); + return new DataTableResponse<>(records, input); } + + @ResponseBody + @GetMapping(value = "/idevid-certificates/list", + produces = MediaType.APPLICATION_JSON_VALUE) + public DataTableResponse getIDevIdCertificatesTableData( + final DataTableInput input) { + + log.debug("Handling list request: {}", input); + + // attempt to get the column property based on the order index. + String orderColumnName = input.getOrderColumnName(); + + log.debug("Ordering on column: {}", orderColumnName); + + String searchText = input.getSearch().getValue(); + List searchableColumns = findSearchableColumnsNames(input.getColumns()); + + int currentPage = input.getStart() / input.getLength(); + Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); + + FilteredRecordsList records = new FilteredRecordsList<>(); + org.springframework.data.domain.Page pagedResult; + + if (StringUtils.isBlank(searchText)) { + pagedResult = + this.iDevIDCertificateRepository.findByArchiveFlag(false, pageable); + } else { + pagedResult = + this.certificateService.findBySearchableColumnsAndArchiveFlag( + IDevIDCertificate.class, + searchableColumns, + searchText, + false, pageable); + } + + if (pagedResult.hasContent()) { + records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); + } + + records.setRecordsFiltered(iDevIDCertificateRepository.findByArchiveFlag(false).size()); + + log.debug("Returning the size of the list of IDEVID certificates: {}", records.size()); + return new DataTableResponse<>(records, input); + } + + @ResponseBody + @GetMapping(value = "/issued-certificates/list", + produces = MediaType.APPLICATION_JSON_VALUE) + public DataTableResponse getIssuedCertificatesTableData( + final DataTableInput input) { + log.debug("Handling list request: {}", input); + + // attempt to get the column property based on the order index. + String orderColumnName = input.getOrderColumnName(); + + log.debug("Ordering on column: {}", orderColumnName); + + String searchText = input.getSearch().getValue(); + List searchableColumns = findSearchableColumnsNames(input.getColumns()); + + int currentPage = input.getStart() / input.getLength(); + Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); + + FilteredRecordsList records = new FilteredRecordsList<>(); + org.springframework.data.domain.Page pagedResult; + + if (StringUtils.isBlank(searchText)) { + pagedResult = + this.issuedCertificateRepository.findByArchiveFlag(false, pageable); + } else { + pagedResult = + this.certificateService.findBySearchableColumnsAndArchiveFlag( + IssuedAttestationCertificate.class, + searchableColumns, + searchText, + false, pageable); + } + + if (pagedResult.hasContent()) { + records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); + } + + records.setRecordsFiltered(issuedCertificateRepository.findByArchiveFlag(false).size()); + + log.debug("Returning the size of the list of issued certificates: {}", records.size()); + return new DataTableResponse<>(records, input); + + } + + /** * Upload and processes a credential. * @@ -631,7 +549,7 @@ public class CertificatePageController extends PageController { * file name) * @throws IOException when writing to response output stream */ - @RequestMapping(value = "/trust-chain/bulk", method = RequestMethod.GET) + @RequestMapping(value = "/trust-chain/bulk-download", method = RequestMethod.GET) public void caBulkDownload(final HttpServletResponse response) throws IOException { log.info("Handling request to download all trust chain certificates"); @@ -655,37 +573,6 @@ public class CertificatePageController extends PageController { } } - /** - * Handles request to download the certs by writing it to the response stream - * for download in bulk. - * - * @param response the response object (needed to update the header with the - * file name) - * @throws IOException when writing to response output stream - */ - @RequestMapping(value = "/platform-credentials/bulk", method = RequestMethod.GET) - public void pcBulkDownload(final HttpServletResponse response) - throws IOException { - log.info("Handling request to download all platform certificates"); - String fileName = "platform_certificates.zip"; - final String singleFileName = "Platform_Certificate"; - String zipFileName; - - // Set filename for download. - response.setHeader("Content-Disposition", "attachment; filename=" + fileName); - response.setContentType("application/zip"); - - try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { - // get all files - bulkDownload(zipOut, this.certificateRepository.findByType("PlatformCredential"), singleFileName); - // write cert to output stream - } catch (IllegalArgumentException ex) { - String uuidError = "Failed to parse ID from: "; - log.error(uuidError, ex); - // send a 404 error when invalid certificate - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } /** * Handles request to download the certs by writing it to the response stream @@ -695,7 +582,7 @@ public class CertificatePageController extends PageController { * file name) * @throws IOException when writing to response output stream */ - @RequestMapping(value = "/issued-certificates/bulk", method = RequestMethod.GET) + @RequestMapping(value = "/issued-certificates/bulk-download", method = RequestMethod.GET) public void icBulkDownload(final HttpServletResponse response) throws IOException { log.info("Handling request to download all issued certificates"); @@ -720,38 +607,6 @@ public class CertificatePageController extends PageController { } } - /** - * Handles request to download the certs by writing it to the response stream - * for download in bulk. - * - * @param response the response object (needed to update the header with the - * file name) - * @throws IOException when writing to response output stream - */ - @RequestMapping(value = "/endorsement-key-credentials/bulk", method = RequestMethod.GET) - public void ekBulkDownload(final HttpServletResponse response) - throws IOException { - log.info("Handling request to download all endorsement certificates"); - String fileName = "endorsement_certificates.zip"; - final String singleFileName = "Endorsement_Certificates"; - - // Set filename for download. - response.setHeader("Content-Disposition", "attachment; filename=" + fileName); - response.setContentType("application/zip"); - - try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { - // get all files - bulkDownload(zipOut, this.certificateRepository.findByType("EndorsementCredential"), - singleFileName); - // write cert to output stream - } catch (IllegalArgumentException ex) { - String uuidError = "Failed to parse ID from: "; - log.error(uuidError, ex); - // send a 404 error when invalid certificate - response.sendError(HttpServletResponse.SC_NOT_FOUND); - } - } - /** * Helper method that packages a collection of certificates into a zip file. * @@ -809,12 +664,6 @@ public class CertificatePageController extends PageController { final int certificateHash) { return switch (certificateType) { - case PLATFORMCREDENTIAL -> this.certificateRepository - .findByCertificateHash(certificateHash, - "PlatformCredential"); - case ENDORSEMENTCREDENTIAL -> this.certificateRepository - .findByCertificateHash(certificateHash, - "EndorsementCredential"); case TRUSTCHAIN -> this.certificateRepository .findByCertificateHash(certificateHash, "CertificateAuthorityCredential"); @@ -825,28 +674,6 @@ public class CertificatePageController extends PageController { }; } - /** - * Gets the certificate by the platform serial number. - * - * @param certificateType String containing the certificate type - * @param serialNumber the platform serial number - * @return the certificate or null if none is found - */ - private List getCertificateByBoardSN( - final String certificateType, - final String serialNumber) { - List associatedCertificates = new LinkedList<>(); - - if (serialNumber != null) { - if (certificateType.equals(PLATFORMCREDENTIAL)) { - associatedCertificates.addAll(this.certificateRepository - .byBoardSerialNumber(serialNumber)); - } - } - - return associatedCertificates; - } - /** * Parses an uploaded file into a certificate and populates the given model * with error messages if parsing fails. @@ -877,10 +704,6 @@ public class CertificatePageController extends PageController { } try { switch (certificateType) { - case PLATFORMCREDENTIAL: - return new PlatformCredential(fileBytes); - case ENDORSEMENTCREDENTIAL: - return new EndorsementCredential(fileBytes); case IDEVIDCERTIFICATE: return new IDevIDCertificate(fileBytes); case TRUSTCHAIN: @@ -1071,77 +894,4 @@ public class CertificatePageController extends PageController { messages.addError(failMessage); log.error(failMessage); } - - /** - * Helper method that utilizes the components of the provided platform certificate to generate - * a collection of component results and subsequently stores these results in the database. - * - * @param certificate certificate - */ - private void parseAndSaveComponentResults(final Certificate certificate) throws IOException { - PlatformCredential platformCredential; - - if (certificate instanceof PlatformCredential) { - platformCredential = (PlatformCredential) certificate; - List componentResults = componentResultRepository - .findByCertificateSerialNumberAndBoardSerialNumber( - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformSerial()); - - if (componentResults.isEmpty()) { - ComponentResult componentResult; - - if (platformCredential.getPlatformConfigurationV1() != null) { - - List componentIdentifiers = - platformCredential.getComponentIdentifiers(); - - for (ComponentIdentifier componentIdentifier : componentIdentifiers) { - componentResult = new ComponentResult(platformCredential.getPlatformSerial(), - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformChainType(), - componentIdentifier); - componentResult.setFailedValidation(false); - componentResult.setDelta(!platformCredential.isPlatformBase()); - componentResultRepository.save(componentResult); - } - } else if (platformCredential.getPlatformConfigurationV2() != null) { - - List componentIdentifiersV2 = - platformCredential.getComponentIdentifiersV2(); - - for (ComponentIdentifierV2 componentIdentifierV2 : componentIdentifiersV2) { - componentResult = new ComponentResult(platformCredential.getPlatformSerial(), - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformChainType(), - componentIdentifierV2); - componentResult.setFailedValidation(false); - componentResult.setDelta(!platformCredential.isPlatformBase()); - componentResultRepository.save(componentResult); - } - } - } else { - for (ComponentResult componentResult : componentResults) { - componentResult.restore(); - componentResult.resetCreateTime(); - componentResultRepository.save(componentResult); - } - } - } - } - - /** - * Helper method that deletes component results based on the provided platform serial number. - * - * @param platformSerial platform serial number - */ - private void deleteComponentResults(final String platformSerial) { - List componentResults = componentResultRepository - .findByBoardSerialNumber(platformSerial); - - for (ComponentResult componentResult : componentResults) { - componentResult.archive(); - componentResultRepository.save(componentResult); - } - } } diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/EndorsementCredentialPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/EndorsementCredentialPageController.java new file mode 100644 index 00000000..ffbac483 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/EndorsementCredentialPageController.java @@ -0,0 +1,397 @@ +package hirs.attestationca.portal.page.controllers; + +import hirs.attestationca.persist.DBManagerException; +import hirs.attestationca.persist.FilteredRecordsList; +import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.EndorsementCredentialRepository; +import hirs.attestationca.persist.entity.userdefined.Certificate; +import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; +import hirs.attestationca.persist.service.CertificateService; +import hirs.attestationca.portal.datatables.Column; +import hirs.attestationca.portal.datatables.DataTableInput; +import hirs.attestationca.portal.datatables.DataTableResponse; +import hirs.attestationca.portal.page.Page; +import hirs.attestationca.portal.page.PageController; +import hirs.attestationca.portal.page.PageMessages; +import hirs.attestationca.portal.page.params.NoPageParams; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.util.encoders.DecoderException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.StreamUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.view.RedirectView; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@Log4j2 +@Controller +@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/endorsement-key-credentials") +public class EndorsementCredentialPageController extends PageController { + + private static final String ENDORSEMENTCREDENTIAL = "endorsement-key-credentials"; + + private final CertificateRepository certificateRepository; + private final EndorsementCredentialRepository endorsementCredentialRepository; + private final CertificateService certificateService; + + @Autowired + public EndorsementCredentialPageController( + final CertificateRepository certificateRepository, + final EndorsementCredentialRepository endorsementCredentialRepository, + final CertificateService certificateService) { + super(Page.TRUST_CHAIN); + this.certificateRepository = certificateRepository; + this.endorsementCredentialRepository = endorsementCredentialRepository; + this.certificateService = certificateService; + } + + /** + * Returns the path for the view and the data model for the page. + * + * @param params The object to map url parameters into. + * @param model The data model for the request. Can contain data from + * redirect. + * @return the path for the view and data model for the page. + */ + @RequestMapping + public ModelAndView initPage( + final NoPageParams params, final Model model) { + return getBaseModelAndView(Page.ENDORSEMENT_KEY_CREDENTIALS); + } + + @ResponseBody + @GetMapping(value = "/list", + produces = MediaType.APPLICATION_JSON_VALUE) + public DataTableResponse getEndorsementCredentialsTableData( + final DataTableInput input) { + + log.debug("Handling list request: {}", input); + + // attempt to get the column property based on the order index. + String orderColumnName = input.getOrderColumnName(); + + log.debug("Ordering on column: {}", orderColumnName); + + String searchText = input.getSearch().getValue(); + List searchableColumns = findSearchableColumnsNames(input.getColumns()); + + int currentPage = input.getStart() / input.getLength(); + Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); + + FilteredRecordsList records = new FilteredRecordsList<>(); + + org.springframework.data.domain.Page pagedResult; + + if (StringUtils.isBlank(searchText)) { + pagedResult = this.endorsementCredentialRepository.findByArchiveFlag(false, pageable); + } else { + pagedResult = + this.certificateService.findBySearchableColumnsAndArchiveFlag( + EndorsementCredential.class, + searchableColumns, + searchText, + false, pageable); + } + + if (pagedResult.hasContent()) { + records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); + } + + records.setRecordsFiltered(endorsementCredentialRepository.findByArchiveFlag(false).size()); + + log.debug("Returning the size of the list of endorsement credentials: {}", records.size()); + return new DataTableResponse<>(records, input); + } + + /** + * Handles request to download the cert by writing it to the response stream + * for download. + * + * @param id the UUID of the cert to download + * @param response the response object (needed to update the header with the + * file name) + * @throws IOException when writing to response output stream + */ + @GetMapping("/download") + public void ekSingleDownload( + @RequestParam final String id, + final HttpServletResponse response) + throws IOException { + log.info("Handling request to download endorsement credential id {}", id); + + try { + UUID uuid = UUID.fromString(id); + Certificate certificate = certificateRepository.getCertificate(uuid); + + if (certificate == null) { + // Use the term "record" here to avoid user confusion b/t cert and cred + String notFoundMessage = "Unable to locate record with ID: " + uuid; + log.warn(notFoundMessage); + // send a 404 error when invalid certificate + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } else { + if (certificate instanceof EndorsementCredential uploadedEndorsementCredential) { + String fileName = "filename=\"" + EndorsementCredential.class.getSimpleName() + + "_" + + uploadedEndorsementCredential.getSerialNumber() + + ".cer\""; + + // Set filename for download. + response.setHeader("Content-Disposition", "attachment;" + fileName); + response.setContentType("application/octet-stream"); + + // write cert to output stream + response.getOutputStream().write(certificate.getRawBytes()); + } + } + } catch (IllegalArgumentException ex) { + String uuidError = "Failed to parse ID from: " + id; + log.error(uuidError, ex); + // send a 404 error when invalid certificate + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * Handles request to download the certs by writing it to the response stream + * for download in bulk. + * + * @param response the response object (needed to update the header with the + * file name) + * @throws IOException when writing to response output stream + */ + @GetMapping("/bulk-download") + public void ekBulkDownload(final HttpServletResponse response) + throws IOException { + log.info("Handling request to download all endorsement certificates"); + + final String fileName = "endorsement_certificates.zip"; + + // Set filename for download. + response.setHeader("Content-Disposition", "attachment; filename=" + fileName); + response.setContentType("application/zip"); + + try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { + + // find all the uploaded endorsement certificates + List certificates = this.certificateRepository.findByType("EndorsementCredential"); + + // convert the list of certificates to a list of endorsement certificates + List uploadedEKs = certificates.stream() + .filter(eachPC -> eachPC instanceof EndorsementCredential) + .map(eachPC -> (EndorsementCredential) eachPC).toList(); + + // get all files + bulkDownload(zipOut, uploadedEKs); + // write cert to output stream + } catch (IllegalArgumentException ex) { + String uuidError = "Failed to parse ID from: "; + log.error(uuidError, ex); + // send a 404 error when invalid certificate + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * Upload and processes a credential. + * + * @param files the files to process + * @param attr the redirection attributes + * @return the redirection view + * @throws URISyntaxException if malformed URI + */ + @PostMapping("/upload") + protected RedirectView upload( + @RequestParam("file") final MultipartFile[] files, + final RedirectAttributes attr) throws URISyntaxException { + + log.info("Handling request to upload one or more endorsement certificates"); + + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + + List errorMessages = new ArrayList<>(); + List successMessages = new ArrayList<>(); + + for (MultipartFile file : files) { + //Parse certificate + EndorsementCredential parseEndorsementCredential = parseEndorsementCredential(file, messages); + + //Store only if it was parsed + if (parseEndorsementCredential != null) { + certificateService.storeCertificate( + ENDORSEMENTCREDENTIAL, + file.getOriginalFilename(), + successMessages, errorMessages, parseEndorsementCredential); + } + } + + //Add messages to the model + model.put(MESSAGES_ATTRIBUTE, messages); + + return redirectTo(Page.ENDORSEMENT_KEY_CREDENTIALS, new NoPageParams(), model, attr); + } + + /** + * Archives (soft delete) the credential. + * + * @param id the UUID of the cert to delete + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return redirect to this page + * @throws URISyntaxException if malformed URI + */ + @PostMapping("/delete") + public RedirectView delete( + @RequestParam final String id, + final RedirectAttributes attr) throws URISyntaxException { + log.info("Handling request to delete endorsement certificate id {}", id); + + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + + try { + List successMessages = new ArrayList<>(); + List errorMessages = new ArrayList<>(); + + UUID uuid = UUID.fromString(id); + + this.certificateService.deleteCertificate(uuid, ENDORSEMENTCREDENTIAL, + successMessages, errorMessages); + + } catch (IllegalArgumentException ex) { + String uuidError = "Failed to parse ID from: " + id; + messages.addError(uuidError); + log.error(uuidError, ex); + } catch (DBManagerException ex) { + String dbError = "Failed to archive cert: " + id; + messages.addError(dbError); + log.error(dbError, ex); + } + + model.put(MESSAGES_ATTRIBUTE, messages); + return redirectTo(Page.ENDORSEMENT_KEY_CREDENTIALS, new NoPageParams(), model, attr); + } + + /** + * Helper method that returns a list of column names that are searchable. + * + * @return searchable column names + */ + private List findSearchableColumnsNames(List columns) { + + // Retrieve all searchable columns and collect their names into a list of strings. + return columns.stream().filter(Column::isSearchable).map(Column::getName) + .collect(Collectors.toList()); + } + + + /** + * Attempts to parse the provided file in order to create an Endorsement Credential. + * + * @param file file + * @param messages page messages + * @return endorsement credential + */ + private EndorsementCredential parseEndorsementCredential(MultipartFile file, PageMessages messages) { + log.info("Received endorsement certificate file of size: {}", file.getSize()); + + byte[] fileBytes; + String fileName = file.getOriginalFilename(); + + // attempt to retrieve file bytes from the provided file + try { + fileBytes = file.getBytes(); + } catch (IOException ioEx) { + final String failMessage = String.format( + "Failed to read uploaded endorsement certificate file (%s): ", fileName); + log.error(failMessage, ioEx); + messages.addError(failMessage + ioEx.getMessage()); + return null; + } + + // attempt to build the endorsement certificate from the uploaded bytes + try { + return new EndorsementCredential(fileBytes); + } catch (IOException ioEx) { + final String failMessage = String.format( + "Failed to parse uploaded endorsement certificate file (%s): ", fileName); + log.error(failMessage, ioEx); + messages.addError(failMessage + ioEx.getMessage()); + return null; + } catch (DecoderException dEx) { + final String failMessage = String.format( + "Failed to parse uploaded endorsement certificate pem file (%s): ", fileName); + log.error(failMessage, dEx); + messages.addError(failMessage + dEx.getMessage()); + return null; + } catch (IllegalArgumentException iaEx) { + final String failMessage = String.format( + "endorsement certificate format not recognized(%s): ", fileName); + log.error(failMessage, iaEx); + messages.addError(failMessage + iaEx.getMessage()); + return null; + } catch (IllegalStateException isEx) { + final String failMessage = String.format( + "Unexpected object while parsing endorsement certificate %s ", fileName); + log.error(failMessage, isEx); + messages.addError(failMessage + isEx.getMessage()); + return null; + } + } + + /** + * Helper method that packages a collection of certificates into a zip file. + * + * @param zipOut zip outputs stream + * @param endorsementCredentials collection of endorsement certificates + * @throws IOException if there are any issues packaging or downloading the zip file + */ + private void bulkDownload(final ZipOutputStream zipOut, + final List endorsementCredentials) throws IOException { + String zipFileName; + final String singleFileName = "Endorsement_Certificates"; + + // get all files + for (EndorsementCredential endorsementCredential : endorsementCredentials) { + zipFileName = String.format("%s[%s].cer", singleFileName, + Integer.toHexString(endorsementCredential.getCertificateHash())); + // configure the zip entry, the properties of the 'file' + ZipEntry zipEntry = new ZipEntry(zipFileName); + zipEntry.setSize((long) endorsementCredential.getRawBytes().length * Byte.SIZE); + zipEntry.setTime(System.currentTimeMillis()); + zipOut.putNextEntry(zipEntry); + // the content of the resource + StreamUtils.copy(endorsementCredential.getRawBytes(), zipOut); + zipOut.closeEntry(); + } + zipOut.finish(); + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IDevIdCertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IDevIdCertificatePageController.java new file mode 100644 index 00000000..147ccf37 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IDevIdCertificatePageController.java @@ -0,0 +1,119 @@ +//package hirs.attestationca.portal.page.controllers; +// +//import hirs.attestationca.persist.FilteredRecordsList; +//import hirs.attestationca.persist.entity.manager.IDevIDCertificateRepository; +//import hirs.attestationca.persist.entity.userdefined.certificate.IDevIDCertificate; +//import hirs.attestationca.persist.service.CertificateService; +//import hirs.attestationca.portal.datatables.Column; +//import hirs.attestationca.portal.datatables.DataTableInput; +//import hirs.attestationca.portal.datatables.DataTableResponse; +//import hirs.attestationca.portal.page.Page; +//import hirs.attestationca.portal.page.PageController; +//import hirs.attestationca.portal.page.params.NoPageParams; +//import lombok.extern.log4j.Log4j2; +//import org.apache.commons.lang3.StringUtils; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.domain.PageRequest; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.domain.Sort; +//import org.springframework.http.MediaType; +//import org.springframework.stereotype.Controller; +//import org.springframework.ui.Model; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.ResponseBody; +//import org.springframework.web.servlet.ModelAndView; +// +//import java.util.List; +//import java.util.stream.Collectors; +// +//@Log4j2 +//@Controller +//@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/idevid-certificates") +//public class IDevIdCertificatePageController extends PageController { +// +// private final IDevIDCertificateRepository iDevIDCertificateRepository; +// private final CertificateService certificateService; +// +// @Autowired +// public IDevIdCertificatePageController(final IDevIDCertificateRepository iDevIDCertificateRepository, +// final CertificateService certificateService) { +// super(Page.TRUST_CHAIN); +// this.iDevIDCertificateRepository = iDevIDCertificateRepository; +// this.certificateService = certificateService; +// } +// +// /** +// * Returns the path for the view and the data model for the page. +// * +// * @param params The object to map url parameters into. +// * @param model The data model for the request. Can contain data from +// * redirect. +// * @return the path for the view and data model for the page. +// */ +// @RequestMapping +// public ModelAndView initPage( +// final NoPageParams params, final Model model) { +// return getBaseModelAndView(Page.IDEVID_CERTIFICATES); +// } +// +// +// @ResponseBody +// @GetMapping(value = "/list", +// produces = MediaType.APPLICATION_JSON_VALUE) +// public DataTableResponse getIDevIdCertificatesTableData( +// final DataTableInput input) { +// +// log.debug("Handling list request: {}", input); +// +// // attempt to get the column property based on the order index. +// String orderColumnName = input.getOrderColumnName(); +// +// log.debug("Ordering on column: {}", orderColumnName); +// +// String searchText = input.getSearch().getValue(); +// List searchableColumns = findSearchableColumnsNames(input.getColumns()); +// +// int currentPage = input.getStart() / input.getLength(); +// Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); +// +// FilteredRecordsList records = new FilteredRecordsList<>(); +// org.springframework.data.domain.Page pagedResult; +// +// if (StringUtils.isBlank(searchText)) { +// pagedResult = +// this.iDevIDCertificateRepository.findByArchiveFlag(false, pageable); +// } else { +// pagedResult = +// this.certificateService.findBySearchableColumnsAndArchiveFlag( +// IDevIDCertificate.class, +// searchableColumns, +// searchText, +// false, pageable); +// } +// +// if (pagedResult.hasContent()) { +// records.addAll(pagedResult.getContent()); +// records.setRecordsTotal(pagedResult.getContent().size()); +// } else { +// records.setRecordsTotal(input.getLength()); +// } +// +// records.setRecordsFiltered(iDevIDCertificateRepository.findByArchiveFlag(false).size()); +// +// log.debug("Returning the size of the list of IDEVID certificates: {}", records.size()); +// return new DataTableResponse<>(records, input); +// } +// +// /** +// * Helper method that returns a list of column names that are searchable. +// * +// * @return searchable column names +// */ +// private List findSearchableColumnsNames(List columns) { +// +// // Retrieve all searchable columns and collect their names into a list of strings. +// return columns.stream().filter(Column::isSearchable).map(Column::getName) +// .collect(Collectors.toList()); +// } +//} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IssuedCertificateController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IssuedCertificateController.java new file mode 100644 index 00000000..26855e44 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IssuedCertificateController.java @@ -0,0 +1,241 @@ +//package hirs.attestationca.portal.page.controllers; +// +//import hirs.attestationca.persist.FilteredRecordsList; +//import hirs.attestationca.persist.entity.manager.CertificateRepository; +//import hirs.attestationca.persist.entity.manager.IssuedCertificateRepository; +//import hirs.attestationca.persist.entity.userdefined.Certificate; +//import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate; +//import hirs.attestationca.persist.service.CertificateService; +//import hirs.attestationca.portal.datatables.Column; +//import hirs.attestationca.portal.datatables.DataTableInput; +//import hirs.attestationca.portal.datatables.DataTableResponse; +//import hirs.attestationca.portal.page.Page; +//import hirs.attestationca.portal.page.PageController; +//import hirs.attestationca.portal.page.params.NoPageParams; +//import jakarta.servlet.http.HttpServletResponse; +//import lombok.extern.log4j.Log4j2; +//import org.apache.commons.lang3.StringUtils; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.domain.PageRequest; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.domain.Sort; +//import org.springframework.http.MediaType; +//import org.springframework.stereotype.Controller; +//import org.springframework.ui.Model; +//import org.springframework.util.StreamUtils; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RequestParam; +//import org.springframework.web.bind.annotation.ResponseBody; +//import org.springframework.web.servlet.ModelAndView; +// +//import java.io.IOException; +//import java.util.List; +//import java.util.UUID; +//import java.util.stream.Collectors; +//import java.util.zip.ZipEntry; +//import java.util.zip.ZipOutputStream; +// +//@Log4j2 +//@Controller +//@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/issued-certificates") +//public class IssuedCertificateController extends PageController { +// +// private final CertificateRepository certificateRepository; +// private final IssuedCertificateRepository issuedCertificateRepository; +// private final CertificateService certificateService; +// +// @Autowired +// public IssuedCertificateController( +// final CertificateRepository certificateRepository, +// final IssuedCertificateRepository issuedCertificateRepository, +// final CertificateService certificateService) { +// super(Page.TRUST_CHAIN); +// this.certificateRepository = certificateRepository; +// this.issuedCertificateRepository = issuedCertificateRepository; +// this.certificateService = certificateService; +// } +// +// /** +// * Returns the path for the view and the data model for the page. +// * +// * @param params The object to map url parameters into. +// * @param model The data model for the request. Can contain data from +// * redirect. +// * @return the path for the view and data model for the page. +// */ +// @RequestMapping +// public ModelAndView initPage( +// final NoPageParams params, final Model model) { +// return getBaseModelAndView(Page.ISSUED_CERTIFICATES); +// } +// +// @ResponseBody +// @GetMapping(value = "/list", +// produces = MediaType.APPLICATION_JSON_VALUE) +// public DataTableResponse getIssuedCertificatesTableData( +// final DataTableInput input) { +// log.debug("Handling list request for issued certificates: {}", input); +// +// // attempt to get the column property based on the order index. +// String orderColumnName = input.getOrderColumnName(); +// +// log.debug("Ordering on column: {}", orderColumnName); +// +// String searchText = input.getSearch().getValue(); +// List searchableColumns = findSearchableColumnsNames(input.getColumns()); +// +// int currentPage = input.getStart() / input.getLength(); +// Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); +// +// FilteredRecordsList records = new FilteredRecordsList<>(); +// org.springframework.data.domain.Page pagedResult; +// +// if (StringUtils.isBlank(searchText)) { +// pagedResult = +// this.issuedCertificateRepository.findByArchiveFlag(false, pageable); +// } else { +// pagedResult = +// this.certificateService.findBySearchableColumnsAndArchiveFlag( +// IssuedAttestationCertificate.class, +// searchableColumns, +// searchText, +// false, pageable); +// } +// +// if (pagedResult.hasContent()) { +// records.addAll(pagedResult.getContent()); +// records.setRecordsTotal(pagedResult.getContent().size()); +// } else { +// records.setRecordsTotal(input.getLength()); +// } +// +// records.setRecordsFiltered(issuedCertificateRepository.findByArchiveFlag(false).size()); +// +// log.debug("Returning the size of the list of issued certificates: {}", records.size()); +// return new DataTableResponse<>(records, input); +// +// } +// +// /** +// * Handles request to download the cert by writing it to the response stream +// * for download. +// * +// * @param id the UUID of the cert to download +// * @param response the response object (needed to update the header with the +// * file name) +// * @throws IOException when writing to response output stream +// */ +// @GetMapping("/download") +// public void download( +// @RequestParam final String id, +// final HttpServletResponse response) +// throws IOException { +// log.info("Handling request to download {}", id); +// +// try { +// UUID uuid = UUID.fromString(id); +// Certificate certificate = certificateRepository.getCertificate(uuid); +// if (certificate == null) { +// // Use the term "record" here to avoid user confusion b/t cert and cred +// String notFoundMessage = "Unable to locate record with ID: " + uuid; +// log.warn(notFoundMessage); +// // send a 404 error when invalid certificate +// response.sendError(HttpServletResponse.SC_NOT_FOUND); +// } else { +// String fileName = "filename=\"" + getCertificateClass(certificateType).getSimpleName() +// + "_" +// + certificate.getSerialNumber() +// + ".cer\""; +// +// // Set filename for download. +// response.setHeader("Content-Disposition", "attachment;" + fileName); +// response.setContentType("application/octet-stream"); +// +// // write cert to output stream +// response.getOutputStream().write(certificate.getRawBytes()); +// } +// } catch (IllegalArgumentException ex) { +// String uuidError = "Failed to parse ID from: " + id; +// log.error(uuidError, ex); +// // send a 404 error when invalid certificate +// response.sendError(HttpServletResponse.SC_NOT_FOUND); +// } +// } +// +// /** +// * Handles request to download the certs by writing it to the response stream +// * for download in bulk. +// * +// * @param response the response object (needed to update the header with the +// * file name) +// * @throws IOException when writing to response output stream +// */ +// @GetMapping("/bulk-download") +// public void icBulkDownload(final HttpServletResponse response) +// throws IOException { +// log.info("Handling request to download all issued certificates"); +// String fileName = "issued_certificates.zip"; +// final String singleFileName = "Issued_Certificate"; +// String zipFileName; +// +// // Set filename for download. +// response.setHeader("Content-Disposition", "attachment; filename=" + fileName); +// response.setContentType("application/zip"); +// +// try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { +// // get all files +// bulkDownload(zipOut, this.certificateRepository.findByType("IssuedAttestationCertificate"), +// singleFileName); +// // write cert to output stream +// } catch (IllegalArgumentException ex) { +// String uuidError = "Failed to parse ID from: "; +// log.error(uuidError, ex); +// // send a 404 error when invalid certificate +// response.sendError(HttpServletResponse.SC_NOT_FOUND); +// } +// } +// +// /** +// * Helper method that returns a list of column names that are searchable. +// * +// * @return searchable column names +// */ +// private List findSearchableColumnsNames(List columns) { +// +// // Retrieve all searchable columns and collect their names into a list of strings. +// return columns.stream().filter(Column::isSearchable).map(Column::getName) +// .collect(Collectors.toList()); +// } +// +// +// /** +// * Helper method that packages a collection of certificates into a zip file. +// * +// * @param zipOut zip outputs stream +// * @param certificates collection of certificates +// * @param singleFileName zip file name +// * @return zip outputs stream +// * @throws IOException if there are any issues packaging or downloading the zip file +// */ +// private ZipOutputStream bulkDownload(final ZipOutputStream zipOut, +// final List certificates, +// final String singleFileName) throws IOException { +// String zipFileName; +// // get all files +// for (Certificate certificate : certificates) { +// zipFileName = String.format("%s[%s].cer", singleFileName, +// Integer.toHexString(certificate.getCertificateHash())); +// // configure the zip entry, the properties of the 'file' +// ZipEntry zipEntry = new ZipEntry(zipFileName); +// zipEntry.setSize((long) certificate.getRawBytes().length * Byte.SIZE); +// zipEntry.setTime(System.currentTimeMillis()); +// zipOut.putNextEntry(zipEntry); +// // the content of the resource +// StreamUtils.copy(certificate.getRawBytes(), zipOut); +// zipOut.closeEntry(); +// } +// zipOut.finish(); +// return zipOut; +// } +//} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PlatformCredentialPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PlatformCredentialPageController.java new file mode 100644 index 00000000..3c418c77 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PlatformCredentialPageController.java @@ -0,0 +1,417 @@ +package hirs.attestationca.portal.page.controllers; + +import hirs.attestationca.persist.DBManagerException; +import hirs.attestationca.persist.FilteredRecordsList; +import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.EndorsementCredentialRepository; +import hirs.attestationca.persist.entity.manager.PlatformCertificateRepository; +import hirs.attestationca.persist.entity.userdefined.Certificate; +import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; +import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; +import hirs.attestationca.persist.service.CertificateService; +import hirs.attestationca.portal.datatables.Column; +import hirs.attestationca.portal.datatables.DataTableInput; +import hirs.attestationca.portal.datatables.DataTableResponse; +import hirs.attestationca.portal.page.Page; +import hirs.attestationca.portal.page.PageController; +import hirs.attestationca.portal.page.PageMessages; +import hirs.attestationca.portal.page.params.NoPageParams; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.util.encoders.DecoderException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.StreamUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.view.RedirectView; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@Log4j2 +@Controller +@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/platform-credentials") +public class PlatformCredentialPageController extends PageController { + + private static final String PLATFORMCREDENTIAL = "platform-credentials"; + + private final CertificateRepository certificateRepository; + private final PlatformCertificateRepository platformCertificateRepository; + private final EndorsementCredentialRepository endorsementCredentialRepository; + private final CertificateService certificateService; + + @Autowired + public PlatformCredentialPageController(final CertificateRepository certificateRepository, + final PlatformCertificateRepository platformCertificateRepository, + final EndorsementCredentialRepository endorsementCredentialRepository, + final CertificateService certificateService) { + super(Page.TRUST_CHAIN); + this.certificateRepository = certificateRepository; + this.platformCertificateRepository = platformCertificateRepository; + this.endorsementCredentialRepository = endorsementCredentialRepository; + this.certificateService = certificateService; + } + + /** + * Returns the path for the view and the data model for the page. + * + * @param params The object to map url parameters into. + * @param model The data model for the request. Can contain data from + * redirect. + * @return the path for the view and data model for the page. + */ + @RequestMapping + public ModelAndView initPage( + final NoPageParams params, final Model model) { + return getBaseModelAndView(Page.PLATFORM_CREDENTIALS); + } + + @ResponseBody + @GetMapping(value = "/list", + produces = MediaType.APPLICATION_JSON_VALUE) + public DataTableResponse getPlatformCredentialsTableData( + final DataTableInput input) { + + log.debug("Handling list request for platform credentials: {}", input); + + // attempt to get the column property based on the order index. + String orderColumnName = input.getOrderColumnName(); + + log.debug("Ordering on column: {}", orderColumnName); + + String searchText = input.getSearch().getValue(); + List searchableColumns = findSearchableColumnsNames(input.getColumns()); + + int currentPage = input.getStart() / input.getLength(); + Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); + + FilteredRecordsList records = new FilteredRecordsList<>(); + + org.springframework.data.domain.Page pagedResult; + + if (StringUtils.isBlank(searchText)) { + pagedResult = + this.platformCertificateRepository.findByArchiveFlag(false, pageable); + } else { + pagedResult = + this.certificateService.findBySearchableColumnsAndArchiveFlag( + PlatformCredential.class, + searchableColumns, + searchText, + false, pageable); + } + + if (pagedResult.hasContent()) { + records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); + } + + records.setRecordsFiltered(platformCertificateRepository.findByArchiveFlag(false).size()); + + EndorsementCredential associatedEC; + + if (!records.isEmpty()) { + // loop all the platform certificates + for (PlatformCredential pc : records) { + // find the EC using the PC's "holder serial number" + associatedEC = this.endorsementCredentialRepository + .findBySerialNumber(pc.getHolderSerialNumber()); + + if (associatedEC != null) { + log.debug("EC ID for holder s/n {} = {}", pc + .getHolderSerialNumber(), associatedEC.getId()); + } + + pc.setEndorsementCredential(associatedEC); + } + } + + log.debug("Returning the size of the list of platform credentials: {}", records.size()); + return new DataTableResponse<>(records, input); + } + + /** + * Handles request to download the cert by writing it to the response stream + * for download. + * + * @param id the UUID of the cert to download + * @param response the response object (needed to update the header with the + * file name) + * @throws IOException when writing to response output stream + */ + @GetMapping("/download") + public void platformCredentialSingleDownload( + @RequestParam final String id, + final HttpServletResponse response) + throws IOException { + log.info("Handling request to download platform certificate id {}", id); + + try { + UUID uuid = UUID.fromString(id); + Certificate certificate = certificateRepository.getCertificate(uuid); + + if (certificate == null) { + log.warn("Unable to locate platform certificate record with ID: {}", uuid); + // send a 404 error when invalid certificate + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } else { + if (certificate instanceof PlatformCredential uploadedPlatformCredential) { + + String fileName = "filename=\"" + PlatformCredential.class.getSimpleName() + + "_" + + uploadedPlatformCredential.getSerialNumber() + + ".cer\""; + + // Set filename for download. + response.setHeader("Content-Disposition", "attachment;" + fileName); + response.setContentType("application/octet-stream"); + + // write cert to output stream + response.getOutputStream().write(certificate.getRawBytes()); + } + } + } catch (IllegalArgumentException ex) { + log.error("Failed to parse platform certificate ID from: " + id, ex); + // send a 404 error when invalid certificate + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * Handles request to download the certs by writing it to the response stream + * for download in bulk. + * + * @param response the response object (needed to update the header with the + * file name) + * @throws IOException when writing to response output stream + */ + @GetMapping("/bulk-download") + public void platformCredentialsBulkDownload(final HttpServletResponse response) + throws IOException { + log.info("Handling request to download all platform certificates"); + + final String fileName = "platform_certificates.zip"; + + // Set filename for download. + response.setHeader("Content-Disposition", "attachment; filename=" + fileName); + response.setContentType("application/zip"); + + try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { + + // find all the uploaded platform certificates + List certificates = this.certificateRepository.findByType("PlatformCredential"); + + // convert the list of certificates to a list of platform certificates + List uploadedPCs = certificates.stream() + .filter(eachPC -> eachPC instanceof PlatformCredential) + .map(eachPC -> (PlatformCredential) eachPC).toList(); + + // get all files and write certificates to output stream + bulkDownloadPlatformCertificates(zipOut, uploadedPCs); + } catch (IllegalArgumentException ex) { + String uuidError = "Failed to parse platform certificate ID from: "; + log.error(uuidError, ex); + // send a 404 error when invalid certificate + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * Upload and processes a credential. + * + * @param files the files to process + * @param attr the redirection attributes + * @return the redirection view + * @throws URISyntaxException if malformed URI + */ + @PostMapping("/upload") + protected RedirectView upload( + @RequestParam("file") final MultipartFile[] files, + final RedirectAttributes attr) throws URISyntaxException { + + log.info("Handling request to upload one or more platform certificates"); + + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + + List errorMessages = new ArrayList<>(); + List successMessages = new ArrayList<>(); + + for (MultipartFile file : files) { + //Parse certificate + PlatformCredential parsedPlatformCredential = parsePlatformCredential(file, messages); + + //Store only if it was parsed + if (parsedPlatformCredential != null) { + certificateService.storeCertificate( + PLATFORMCREDENTIAL, + file.getOriginalFilename(), + successMessages, errorMessages, parsedPlatformCredential); + } + } + + //Add messages to the model + model.put(MESSAGES_ATTRIBUTE, messages); + + return redirectTo(Page.PLATFORM_CREDENTIALS, new NoPageParams(), model, attr); + } + + + /** + * Archives (soft delete) the credential. + * + * @param id the UUID of the cert to delete + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return redirect to this page + * @throws URISyntaxException if malformed URI + */ + @PostMapping("/delete") + public RedirectView delete( + @RequestParam final String id, + final RedirectAttributes attr) throws URISyntaxException { + log.info("Handling request to delete platform certificate id {}", id); + + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + + try { + List successMessages = new ArrayList<>(); + List errorMessages = new ArrayList<>(); + + UUID uuid = UUID.fromString(id); + + this.certificateService.deleteCertificate(uuid, PLATFORMCREDENTIAL, + successMessages, errorMessages); + + } catch (IllegalArgumentException ex) { + String uuidError = "Failed to parse ID from: " + id; + messages.addError(uuidError); + log.error(uuidError, ex); + } catch (DBManagerException ex) { + String dbError = "Failed to archive cert: " + id; + messages.addError(dbError); + log.error(dbError, ex); + } + + model.put(MESSAGES_ATTRIBUTE, messages); + return redirectTo(Page.PLATFORM_CREDENTIALS, new NoPageParams(), model, attr); + } + + /** + * Helper method that returns a list of column names that are searchable. + * + * @return searchable column names + */ + private List findSearchableColumnsNames(List columns) { + // Retrieve all searchable columns and collect their names into a list of strings. + return columns.stream().filter(Column::isSearchable).map(Column::getName) + .collect(Collectors.toList()); + } + + /** + * Attempts to parse the provided file in order to create a PLatform Credential. + * + * @param file file + * @param messages page messages + * @return platform credential + */ + private PlatformCredential parsePlatformCredential(MultipartFile file, PageMessages messages) { + log.info("Received platform certificate file of size: {}", file.getSize()); + + byte[] fileBytes; + String fileName = file.getOriginalFilename(); + + // attempt to retrieve file bytes from the provided file + try { + fileBytes = file.getBytes(); + } catch (IOException ioEx) { + final String failMessage = String.format( + "Failed to read uploaded platform certificate file (%s): ", fileName); + log.error(failMessage, ioEx); + messages.addError(failMessage + ioEx.getMessage()); + return null; + } + + // attempt to build the platform certificate from the uploaded bytes + try { + return new PlatformCredential(fileBytes); + } catch (IOException ioEx) { + final String failMessage = String.format( + "Failed to parse uploaded platform certificate file (%s): ", fileName); + log.error(failMessage, ioEx); + messages.addError(failMessage + ioEx.getMessage()); + return null; + } catch (DecoderException dEx) { + final String failMessage = String.format( + "Failed to parse uploaded platform certificate pem file (%s): ", fileName); + log.error(failMessage, dEx); + messages.addError(failMessage + dEx.getMessage()); + return null; + } catch (IllegalArgumentException iaEx) { + final String failMessage = String.format( + "platform certificate format not recognized(%s): ", fileName); + log.error(failMessage, iaEx); + messages.addError(failMessage + iaEx.getMessage()); + return null; + } catch (IllegalStateException isEx) { + final String failMessage = String.format( + "Unexpected object while parsing platform certificate %s ", fileName); + log.error(failMessage, isEx); + messages.addError(failMessage + isEx.getMessage()); + return null; + } + } + + /** + * Helper method that packages a collection of platform credentials into a zip file. + * + * @param zipOut zip outputs stream + * @param platformCredentials collection of platform certificates + * @throws IOException if there are any issues packaging or downloading the zip file + */ + private void bulkDownloadPlatformCertificates(final ZipOutputStream zipOut, + final List platformCredentials) + throws IOException { + String zipFileName; + final String singleFileName = "Platform_Certificate"; + + // get all files + for (PlatformCredential platformCredential : platformCredentials) { + zipFileName = String.format("%s[%s].cer", singleFileName, + Integer.toHexString(platformCredential.getCertificateHash())); + // configure the zip entry, the properties of the 'file' + ZipEntry zipEntry = new ZipEntry(zipFileName); + zipEntry.setSize((long) platformCredential.getRawBytes().length * Byte.SIZE); + zipEntry.setTime(System.currentTimeMillis()); + zipOut.putNextEntry(zipEntry); + // the content of the resource + StreamUtils.copy(platformCredential.getRawBytes(), zipOut); + zipOut.closeEntry(); + } + zipOut.finish(); + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/TrustChainCertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/TrustChainCertificatePageController.java new file mode 100644 index 00000000..7598059f --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/TrustChainCertificatePageController.java @@ -0,0 +1,286 @@ +//package hirs.attestationca.portal.page.controllers; +// +//import hirs.attestationca.persist.FilteredRecordsList; +//import hirs.attestationca.persist.entity.manager.CACredentialRepository; +//import hirs.attestationca.persist.entity.manager.CertificateRepository; +//import hirs.attestationca.persist.entity.userdefined.Certificate; +//import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; +//import hirs.attestationca.persist.service.CertificateService; +//import hirs.attestationca.portal.datatables.Column; +//import hirs.attestationca.portal.datatables.DataTableInput; +//import hirs.attestationca.portal.datatables.DataTableResponse; +//import hirs.attestationca.portal.page.Page; +//import hirs.attestationca.portal.page.PageController; +//import hirs.attestationca.portal.page.params.NoPageParams; +//import hirs.attestationca.portal.page.utils.CertificateStringMapBuilder; +//import jakarta.servlet.http.HttpServletResponse; +//import lombok.extern.log4j.Log4j2; +//import org.apache.commons.lang3.StringUtils; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.domain.PageRequest; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.domain.Sort; +//import org.springframework.http.MediaType; +//import org.springframework.stereotype.Controller; +//import org.springframework.ui.Model; +//import org.springframework.util.StreamUtils; +//import org.springframework.web.bind.annotation.GetMapping; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RequestParam; +//import org.springframework.web.bind.annotation.ResponseBody; +//import org.springframework.web.servlet.ModelAndView; +// +//import java.io.IOException; +//import java.security.cert.CertificateEncodingException; +//import java.security.cert.X509Certificate; +//import java.util.HashMap; +//import java.util.List; +//import java.util.UUID; +//import java.util.stream.Collectors; +//import java.util.zip.ZipEntry; +//import java.util.zip.ZipOutputStream; +// +//import static hirs.attestationca.portal.page.controllers.CertificatePageController.ACA_CERT_DATA; +// +//@Log4j2 +//@Controller +//@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/trust-chain") +//public class TrustChainCertificatePageController extends PageController { +// +// private final CertificateRepository certificateRepository; +// private final CACredentialRepository caCredentialRepository; +// private final CertificateService certificateService; +// private CertificateAuthorityCredential certificateAuthorityCredential; +// +// @Autowired +// public TrustChainCertificatePageController(final CertificateRepository certificateRepository, +// final CACredentialRepository caCredentialRepository, +// final CertificateService certificateService, +// final X509Certificate acaCertificate) { +// super(Page.TRUST_CHAIN); +// this.certificateRepository = certificateRepository; +// this.caCredentialRepository = caCredentialRepository; +// this.certificateService = certificateService; +// +// try { +// certificateAuthorityCredential +// = new CertificateAuthorityCredential(acaCertificate.getEncoded()); +// } catch (IOException ioEx) { +// log.error("Failed to read ACA certificate", ioEx); +// } catch (CertificateEncodingException ceEx) { +// log.error("Error getting encoded ACA certificate", ceEx); +// } +// } +// +// /** +// * Returns the path for the view and the data model for the page. +// * +// * @param params The object to map url parameters into. +// * @param model The data model for the request. Can contain data from +// * redirect. +// * @return the path for the view and data model for the page. +// */ +// @RequestMapping +// public ModelAndView initPage( +// final NoPageParams params, final Model model) { +// +// ModelAndView mav = getBaseModelAndView(Page.TRUST_CHAIN); +// +// mav.addObject(ACA_CERT_DATA, +// new HashMap(CertificateStringMapBuilder.getCertificateAuthorityInformation( +// certificateAuthorityCredential, this.certificateRepository, +// this.caCredentialRepository))); +// +// return mav; +// } +// +// @ResponseBody +// @GetMapping(value = "/list", +// produces = MediaType.APPLICATION_JSON_VALUE) +// public DataTableResponse getTrustChainTableData( +// final DataTableInput input) { +// log.debug("Handling list request: {}", input); +// +// // attempt to get the column property based on the order index. +// String orderColumnName = input.getOrderColumnName(); +// +// log.debug("Ordering on column: {}", orderColumnName); +// +// String searchText = input.getSearch().getValue(); +// List searchableColumns = findSearchableColumnsNames(input.getColumns()); +// +// int currentPage = input.getStart() / input.getLength(); +// Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); +// +// FilteredRecordsList records = new FilteredRecordsList<>(); +// +// org.springframework.data.domain.Page pagedResult; +// +// if (StringUtils.isBlank(searchText)) { +// pagedResult = +// this.caCredentialRepository.findByArchiveFlag(false, pageable); +// } else { +// pagedResult = +// this.certificateService.findBySearchableColumnsAndArchiveFlag( +// CertificateAuthorityCredential.class, +// searchableColumns, +// searchText, +// false, pageable); +// } +// +// +// if (pagedResult.hasContent()) { +// records.addAll(pagedResult.getContent()); +// records.setRecordsTotal(pagedResult.getContent().size()); +// } else { +// records.setRecordsTotal(input.getLength()); +// } +// +// records.setRecordsFiltered(caCredentialRepository.findByArchiveFlag(false).size()); +// +// log.debug("Returning the size of the list of trust chain certificates: {}", records.size()); +// return new DataTableResponse<>(records, input); +// } +// +// /** +// * Handles request to download the cert by writing it to the response stream +// * for download. +// * +// * @param id the UUID of the cert to download +// * @param response the response object (needed to update the header with the +// * file name) +// * @throws IOException when writing to response output stream +// */ +// @GetMapping("/download") +// public void download( +// @RequestParam final String id, +// final HttpServletResponse response) +// throws IOException { +// log.info("Handling request to download {}", id); +// +// try { +// UUID uuid = UUID.fromString(id); +// Certificate certificate = certificateRepository.getCertificate(uuid); +// if (certificate == null) { +// // Use the term "record" here to avoid user confusion b/t cert and cred +// String notFoundMessage = "Unable to locate record with ID: " + uuid; +// log.warn(notFoundMessage); +// // send a 404 error when invalid certificate +// response.sendError(HttpServletResponse.SC_NOT_FOUND); +// } else { +// String fileName = "filename=\"" + getCertificateClass(certificateType).getSimpleName() +// + "_" +// + certificate.getSerialNumber() +// + ".cer\""; +// +// // Set filename for download. +// response.setHeader("Content-Disposition", "attachment;" + fileName); +// response.setContentType("application/octet-stream"); +// +// // write cert to output stream +// response.getOutputStream().write(certificate.getRawBytes()); +// } +// } catch (IllegalArgumentException ex) { +// String uuidError = "Failed to parse ID from: " + id; +// log.error(uuidError, ex); +// // send a 404 error when invalid certificate +// response.sendError(HttpServletResponse.SC_NOT_FOUND); +// } +// } +// +// /** +// * Handles request to download the ACA cert by writing it to the response +// * stream for download. +// * +// * @param response the response object (needed to update the header with the +// * file name) +// * @throws IOException when writing to response output stream +// */ +// @ResponseBody +// @GetMapping("/download-aca-cert") +// public void downloadAcaCertificate(final HttpServletResponse response) +// throws IOException { +// +// // Set filename for download. +// response.setHeader("Content-Disposition", "attachment; filename=\"hirs-aca-cert.cer\""); +// response.setContentType("application/octet-stream"); +// +// // write cert to output stream +// response.getOutputStream().write(certificateAuthorityCredential.getRawBytes()); +// } +// +// /** +// * Handles request to download the certs by writing it to the response stream +// * for download in bulk. +// * +// * @param response the response object (needed to update the header with the +// * file name) +// * @throws IOException when writing to response output stream +// */ +// @GetMapping("/bulk-download") +// public void caBulkDownload(final HttpServletResponse response) +// throws IOException { +// log.info("Handling request to download all trust chain certificates"); +// String fileName = "trust-chain.zip"; +// final String singleFileName = "ca-certificates"; +// +// // Set filename for download. +// response.setHeader("Content-Disposition", "attachment; filename=" + fileName); +// response.setContentType("application/zip"); +// +// try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { +// // get all files +// bulkDownload(zipOut, this.certificateRepository.findByType("CertificateAuthorityCredential"), +// singleFileName); +// // write cert to output stream +// } catch (IllegalArgumentException ex) { +// String uuidError = "Failed to parse ID from: "; +// log.error(uuidError, ex); +// // send a 404 error when invalid certificate +// response.sendError(HttpServletResponse.SC_NOT_FOUND); +// } +// } +// +// +// /** +// * Helper method that returns a list of column names that are searchable. +// * +// * @return searchable column names +// */ +// private List findSearchableColumnsNames(List columns) { +// +// // Retrieve all searchable columns and collect their names into a list of strings. +// return columns.stream().filter(Column::isSearchable).map(Column::getName) +// .collect(Collectors.toList()); +// } +// +// /** +// * Helper method that packages a collection of certificates into a zip file. +// * +// * @param zipOut zip outputs stream +// * @param certificates collection of certificates +// * @param singleFileName zip file name +// * @return zip outputs stream +// * @throws IOException if there are any issues packaging or downloading the zip file +// */ +// private ZipOutputStream bulkDownload(final ZipOutputStream zipOut, +// final List certificates, +// final String singleFileName) throws IOException { +// String zipFileName; +// // get all files +// for (Certificate certificate : certificates) { +// zipFileName = String.format("%s[%s].cer", singleFileName, +// Integer.toHexString(certificate.getCertificateHash())); +// // configure the zip entry, the properties of the 'file' +// ZipEntry zipEntry = new ZipEntry(zipFileName); +// zipEntry.setSize((long) certificate.getRawBytes().length * Byte.SIZE); +// zipEntry.setTime(System.currentTimeMillis()); +// zipOut.putNextEntry(zipEntry); +// // the content of the resource +// StreamUtils.copy(certificate.getRawBytes(), zipOut); +// zipOut.closeEntry(); +// } +// zipOut.finish(); +// return zipOut; +// } +//} diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/devices.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/devices.jsp index 4b83d9a0..4e6206b0 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/devices.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/devices.jsp @@ -1,4 +1,4 @@ -<%@page contentType="text/html" pageEncoding="UTF-8"%> + + + + + Device Listing - - - - Device Listing + + + + + + + + + + - - - - - - - - - - +
+ + + + + + + + + + + + + +
Validation StatusHostnameCredentials
Issued AttestationPlatformEndorsement
+
+ -
+ return html; + }, + }, + ]; + //Set data tables + setDataTables("#deviceTable", url, columns); + }); + +
diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp index fa6c55f0..2f5d3284 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp @@ -1,4 +1,4 @@ -<%@page contentType="text/html" pageEncoding="UTF-8"%> + + + IDevID Certificates - - - - IDevID Certificates + +
+ + Import IDevID Certificates + + + + + + + +
+
+
+ + + + + + + + + + +
IssuerSubjectValid (begin)Valid (end)Options
+
+ -
-
\ No newline at end of file + //Set data tables + setDataTables("#idevidCertificateTable", url, columns); + }); + + + diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp index 5f5246ff..03e70369 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp @@ -19,7 +19,7 @@
Issued Credentials - +
diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp index c14dc72f..9d7755a8 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp @@ -32,7 +32,7 @@ > - + diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/reference-manifests.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/reference-manifests.jsp index a2417aaa..19f07204 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/reference-manifests.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/reference-manifests.jsp @@ -8,71 +8,99 @@ <%-- CONTENT --%> + + + + Reference Integrity Manifests - - - - Reference Integrity Manifests + + +
+ + Reference Integrity Manifests + + + + + + + +
+
+
+ + + + + + + + + + + +
Tag IDTypeManufacturerModelVersionOptions
+
- - -
- - Reference Integrity Manifests - - - - - - - -
-
-
- - - - - - - - - - - -
Tag IDTypeManufacturerModelVersionOptions
-
+ -
-
\ No newline at end of file + //Set data tables + setDataTables("#referenceManifestTable", url, columns); + }); + +
+ diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/rim-database.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/rim-database.jsp index df719e15..4f84e807 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/rim-database.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/rim-database.jsp @@ -1,4 +1,4 @@ -<%@page contentType="text/html" pageEncoding="UTF-8"%> + + + + + RIM Database - - - - RIM Database + +
+
+ + + + + + + + + + + + +
ManufacturerModelEvent TypePCR IndexDigest ValueBase RIMSupport RIM
+
- -
-
- - - - - - - - - - - - -
ManufacturerModelEvent TypePCR IndexDigest ValueBase RIMSupport RIM
-
+ -
-
\ No newline at end of file + //Set data tables + setDataTables("#digestValueTable", url, columns); + }); + + + diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/trust-chain.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/trust-chain.jsp index bb3a701f..3da5aafd 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/trust-chain.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/trust-chain.jsp @@ -1,4 +1,4 @@ -<%@page contentType="text/html" pageEncoding="UTF-8"%> +