mirror of
https://github.com/nsacyber/HIRS.git
synced 2025-04-13 14:13:09 +00:00
v3_issue_811: successfully placed rest endpoints in their respective controllers. Search feature works fine. Found bug that exists in main branch as well (will fix it in here). Will now need to worry about searching just on individual comments, adding more javadocs/comments, and lastly fixing error/info messages display.
Some checks are pending
Dotnet Provisioner Unit Tests / Restore and Run Unit Tests (ubuntu-20.04) (push) Waiting to run
Dotnet Provisioner Unit Tests / Restore and Run Unit Tests (windows-2022) (push) Waiting to run
Dotnet Provisioner Unit Tests / Evaluate Tests (push) Blocked by required conditions
HIRS Build and Unit Test / ACA_Provisioner_Unit_Tests (push) Waiting to run
HIRS System Tests / DockerTests (push) Waiting to run
Some checks are pending
Dotnet Provisioner Unit Tests / Restore and Run Unit Tests (ubuntu-20.04) (push) Waiting to run
Dotnet Provisioner Unit Tests / Restore and Run Unit Tests (windows-2022) (push) Waiting to run
Dotnet Provisioner Unit Tests / Evaluate Tests (push) Blocked by required conditions
HIRS Build and Unit Test / ACA_Provisioner_Unit_Tests (push) Waiting to run
HIRS System Tests / DockerTests (push) Waiting to run
This commit is contained in:
parent
800010870a
commit
9d4b8eca31
@ -21,11 +21,14 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* Service layer class that handles the storage and retrieval of all types of certificates.
|
||||
@ -34,11 +37,11 @@ import java.util.UUID;
|
||||
@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 static final String TRUST_CHAIN = "trust-chain";
|
||||
private static final String PLATFORM_CREDENTIALS = "platform-credentials";
|
||||
private static final String IDEVID_CERTIFICATES = "idevid-certificates";
|
||||
private static final String ENDORSEMENT_CREDENTIALS = "endorsement-key-credentials";
|
||||
private static final String ISSUED_CERTIFICATES = "issued-certificates";
|
||||
|
||||
private final CertificateRepository certificateRepository;
|
||||
private final ComponentResultRepository componentResultRepository;
|
||||
@ -56,7 +59,7 @@ 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 searchText text that was input in the search textbox
|
||||
* @param archiveFlag archive flag
|
||||
* @param pageable pageable
|
||||
* @param <T> generic entity class
|
||||
@ -101,6 +104,14 @@ public class CertificateService {
|
||||
return new PageImpl<>(resultList, pageable, totalRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uuid
|
||||
* @return
|
||||
*/
|
||||
public Certificate findCertificate(UUID uuid) {
|
||||
return this.certificateRepository.getCertificate(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stored the given certificate in the database.
|
||||
*
|
||||
@ -125,18 +136,18 @@ public class CertificateService {
|
||||
existingCertificate = getCertificateByHash(
|
||||
certificateType,
|
||||
certificate.getCertificateHash());
|
||||
} catch (DBServiceException dbsEx) {
|
||||
} catch (Exception exception) {
|
||||
final String failMessage = "Querying for existing certificate failed ("
|
||||
+ fileName + "): ";
|
||||
errorMessages.add(failMessage + dbsEx.getMessage());
|
||||
log.error(failMessage, dbsEx);
|
||||
errorMessages.add(failMessage + exception.getMessage());
|
||||
log.error(failMessage, exception);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// save the new certificate if no match is found
|
||||
if (existingCertificate == null) {
|
||||
if (certificateType.equals(PLATFORMCREDENTIAL)) {
|
||||
if (certificateType.equals(PLATFORM_CREDENTIALS)) {
|
||||
PlatformCredential platformCertificate = (PlatformCredential) certificate;
|
||||
if (platformCertificate.isPlatformBase()) {
|
||||
List<PlatformCredential> sharedCertificates = getPlatformCertificateByBoardSN(
|
||||
@ -186,6 +197,7 @@ public class CertificateService {
|
||||
existingCertificate.resetCreateTime();
|
||||
this.certificateRepository.save(existingCertificate);
|
||||
|
||||
//todo
|
||||
List<ComponentResult> componentResults = componentResultRepository
|
||||
.findByBoardSerialNumber(((PlatformCredential) existingCertificate)
|
||||
.getPlatformSerial());
|
||||
@ -238,7 +250,7 @@ public class CertificateService {
|
||||
errorMessages.add(notFoundMessage);
|
||||
log.warn(notFoundMessage);
|
||||
} else {
|
||||
if (certificateType.equals(PLATFORMCREDENTIAL)) {
|
||||
if (certificateType.equals(PLATFORM_CREDENTIALS)) {
|
||||
PlatformCredential platformCertificate = (PlatformCredential) certificate;
|
||||
if (platformCertificate.isPlatformBase()) {
|
||||
// only do this if the base is being deleted.
|
||||
@ -266,7 +278,63 @@ public class CertificateService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the certificate by the hash code of its bytes. Looks for both
|
||||
* Helper method that packages a collection of certificates into a zip file.
|
||||
*
|
||||
* @param zipOut zip outputs streams
|
||||
* @param singleFileName zip file name
|
||||
* @throws IOException if there are any issues packaging or downloading the zip file
|
||||
*/
|
||||
public void bulkDownloadCertificates(final ZipOutputStream zipOut,
|
||||
final String certificateType,
|
||||
final String singleFileName) throws IOException {
|
||||
String zipFileName;
|
||||
final List<Certificate> certificates = findCertificatesByType(certificateType);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of certificates based on the certificate type.
|
||||
*
|
||||
* @param certificateType certificate type
|
||||
* @return list of certificates
|
||||
*/
|
||||
private List<Certificate> findCertificatesByType(String certificateType) {
|
||||
return switch (certificateType) {
|
||||
case PLATFORM_CREDENTIALS -> this.certificateRepository
|
||||
.findByType(
|
||||
"PlatformCredential");
|
||||
case ENDORSEMENT_CREDENTIALS -> this.certificateRepository
|
||||
.findByType(
|
||||
"EndorsementCredential");
|
||||
case TRUST_CHAIN -> this.certificateRepository
|
||||
.findByType(
|
||||
"CertificateAuthorityCredential");
|
||||
case IDEVID_CERTIFICATES -> this.certificateRepository
|
||||
.findByType(
|
||||
"IDevIDCertificate");
|
||||
case ISSUED_CERTIFICATES -> this.certificateRepository.
|
||||
findByType("IssuedAttestationCertificate");
|
||||
default -> throw new IllegalArgumentException("The provided certificate type {"
|
||||
+ certificateType + "} does not exist");
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the certificate by the hash code of its bytes. Looks for both
|
||||
* archived and unarchived certificates.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
@ -277,19 +345,20 @@ public class CertificateService {
|
||||
final String certificateType,
|
||||
final int certificateHash) {
|
||||
return switch (certificateType) {
|
||||
case PLATFORMCREDENTIAL -> this.certificateRepository
|
||||
case PLATFORM_CREDENTIALS -> this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"PlatformCredential");
|
||||
case ENDORSEMENTCREDENTIAL -> this.certificateRepository
|
||||
case ENDORSEMENT_CREDENTIALS -> this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"EndorsementCredential");
|
||||
case TRUSTCHAIN -> this.certificateRepository
|
||||
case TRUST_CHAIN -> this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"CertificateAuthorityCredential");
|
||||
case IDEVIDCERTIFICATE -> this.certificateRepository
|
||||
case IDEVID_CERTIFICATES -> this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"IDevIDCertificate");
|
||||
default -> null;
|
||||
default -> throw new IllegalArgumentException("The provided certificate type {"
|
||||
+ certificateType + "} does not exist");
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ public abstract class PageController<P extends PageParams> {
|
||||
String defaultUri = "../" + newPage.getViewName();
|
||||
// create uri with specified parameters
|
||||
URIBuilder uri = new URIBuilder("../" + newPage.getViewName());
|
||||
log.debug("Redirection URI = " + uri.toString());
|
||||
log.debug("Redirection URI = {}", uri.toString());
|
||||
|
||||
if (params != null) {
|
||||
for (Map.Entry<String, ?> e : params.asMap().entrySet()) {
|
||||
|
@ -1,897 +0,0 @@
|
||||
package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
import hirs.attestationca.persist.DBManagerException;
|
||||
import hirs.attestationca.persist.DBServiceException;
|
||||
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.IDevIDCertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.IssuedCertificateRepository;
|
||||
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.IDevIDCertificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.service.CertificateService;
|
||||
import hirs.attestationca.persist.util.CredentialHelper;
|
||||
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 hirs.attestationca.portal.page.utils.CertificateStringMapBuilder;
|
||||
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.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
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.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
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;
|
||||
|
||||
// note uploading base64 certs, old or new having decode issues check ACA channel
|
||||
|
||||
/**
|
||||
* Controller for the Certificates list all pages.
|
||||
*/
|
||||
@Log4j2
|
||||
@Controller
|
||||
@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request")
|
||||
public class CertificatePageController extends PageController<NoPageParams> {
|
||||
/**
|
||||
* Model attribute name used by initPage for the aca cert info.
|
||||
*/
|
||||
static final String ACA_CERT_DATA = "acaCertData";
|
||||
private static final String TRUSTCHAIN = "trust-chain";
|
||||
private static final String IDEVIDCERTIFICATE = "idevid-certificates";
|
||||
private static final String ISSUEDCERTIFICATES = "issued-certificates";
|
||||
private final CertificateRepository certificateRepository;
|
||||
private final ComponentResultRepository componentResultRepository;
|
||||
private final IssuedCertificateRepository issuedCertificateRepository;
|
||||
private final CACredentialRepository caCredentialRepository;
|
||||
private final IDevIDCertificateRepository iDevIDCertificateRepository;
|
||||
private final CertificateService certificateService;
|
||||
private CertificateAuthorityCredential certificateAuthorityCredential;
|
||||
|
||||
/**
|
||||
* Constructor providing the Page's display and routing specification.
|
||||
*
|
||||
* @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 ComponentResultRepository componentResultRepository,
|
||||
final IssuedCertificateRepository issuedCertificateRepository,
|
||||
final CACredentialRepository caCredentialRepository,
|
||||
final IDevIDCertificateRepository iDevIDCertificateRepository,
|
||||
final CertificateService certificateService,
|
||||
final X509Certificate acaCertificate) {
|
||||
super(Page.TRUST_CHAIN);
|
||||
this.certificateRepository = certificateRepository;
|
||||
this.componentResultRepository = componentResultRepository;
|
||||
this.issuedCertificateRepository = issuedCertificateRepository;
|
||||
this.caCredentialRepository = caCredentialRepository;
|
||||
this.iDevIDCertificateRepository = iDevIDCertificateRepository;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the page based on the certificate type.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @return the page for the certificate type.
|
||||
*/
|
||||
private static Page getCertificatePage(final String certificateType) {
|
||||
// get page information (default to TRUST_CHAIN)
|
||||
return switch (certificateType) {
|
||||
case ISSUEDCERTIFICATES -> Page.ISSUED_CERTIFICATES;
|
||||
case IDEVIDCERTIFICATE -> Page.IDEVID_CERTIFICATES;
|
||||
default -> Page.TRUST_CHAIN;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the concrete certificate class type to query for.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @return the certificate class type
|
||||
*/
|
||||
private static Class<? extends Certificate> getCertificateClass(final String certificateType) {
|
||||
return switch (certificateType) {
|
||||
case ISSUEDCERTIFICATES -> IssuedAttestationCertificate.class;
|
||||
case IDEVIDCERTIFICATE -> IDevIDCertificate.class;
|
||||
case TRUSTCHAIN -> CertificateAuthorityCredential.class;
|
||||
default -> throw new IllegalArgumentException(
|
||||
String.format("Unknown certificate type: %s", certificateType));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
@RequestMapping
|
||||
public ModelAndView initPage(final NoPageParams params, final Model model) {
|
||||
return getBaseModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path for the view and the data model for the page.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @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("/{certificateType}")
|
||||
public ModelAndView initPage(@PathVariable("certificateType") final String certificateType,
|
||||
final NoPageParams params, final Model model) {
|
||||
|
||||
ModelAndView mav = null;
|
||||
HashMap<String, String> data = new HashMap<>();
|
||||
// add page information
|
||||
switch (certificateType) {
|
||||
case IDEVIDCERTIFICATE:
|
||||
mav = getBaseModelAndView(Page.IDEVID_CERTIFICATES);
|
||||
break;
|
||||
case ISSUEDCERTIFICATES:
|
||||
mav = getBaseModelAndView(Page.ISSUED_CERTIFICATES);
|
||||
break;
|
||||
case TRUSTCHAIN:
|
||||
mav = getBaseModelAndView(Page.TRUST_CHAIN);
|
||||
// Map with the ACA certificate information
|
||||
data.putAll(CertificateStringMapBuilder.getCertificateAuthorityInformation(
|
||||
certificateAuthorityCredential, this.certificateRepository,
|
||||
this.caCredentialRepository));
|
||||
mav.addObject(ACA_CERT_DATA, data);
|
||||
break;
|
||||
default:
|
||||
// send to an error page
|
||||
break;
|
||||
}
|
||||
|
||||
return mav;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that returns a list of column names that are searchable.
|
||||
*
|
||||
* @return searchable column names
|
||||
*/
|
||||
private List<String> findSearchableColumnsNames(List<Column> 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());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/trust-chain/list",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public DataTableResponse<? extends Certificate> 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<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
|
||||
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
|
||||
FilteredRecordsList<CertificateAuthorityCredential> records = new FilteredRecordsList<>();
|
||||
|
||||
org.springframework.data.domain.Page<CertificateAuthorityCredential> 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);
|
||||
}
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/idevid-certificates/list",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public DataTableResponse<? extends Certificate> 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<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
|
||||
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
|
||||
FilteredRecordsList<IDevIDCertificate> records = new FilteredRecordsList<>();
|
||||
org.springframework.data.domain.Page<IDevIDCertificate> 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<? extends Certificate> 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<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
|
||||
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
|
||||
FilteredRecordsList<IssuedAttestationCertificate> records = new FilteredRecordsList<>();
|
||||
org.springframework.data.domain.Page<IssuedAttestationCertificate> 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.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @param files the files to process
|
||||
* @param attr the redirection attributes
|
||||
* @return the redirection view
|
||||
* @throws URISyntaxException if malformed URI
|
||||
*/
|
||||
@RequestMapping(value = "/{certificateType}/upload", method = RequestMethod.POST)
|
||||
protected RedirectView upload(
|
||||
@PathVariable("certificateType") final String certificateType,
|
||||
@RequestParam("file") final MultipartFile[] files,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
//Parse certificate
|
||||
Certificate certificate = parseCertificate(certificateType, file, messages);
|
||||
|
||||
//Store only if it was parsed
|
||||
if (certificate != null) {
|
||||
storeCertificate(
|
||||
certificateType,
|
||||
file.getOriginalFilename(),
|
||||
messages, certificate);
|
||||
}
|
||||
}
|
||||
|
||||
//Add messages to the model
|
||||
model.put(MESSAGES_ATTRIBUTE, messages);
|
||||
|
||||
return redirectTo(getCertificatePage(certificateType), new NoPageParams(), model, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Archives (soft delete) the credential.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @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
|
||||
*/
|
||||
@RequestMapping(value = "/{certificateType}/delete", method = RequestMethod.POST)
|
||||
public RedirectView delete(
|
||||
@PathVariable("certificateType") final String certificateType,
|
||||
@RequestParam final String id,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
log.info("Handling request to delete {}", id);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
|
||||
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;
|
||||
messages.addError(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<PlatformCredential> sharedCertificates = getCertificateByBoardSN(
|
||||
certificateType,
|
||||
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";
|
||||
messages.addInfo(deleteCompletedMessage);
|
||||
log.info(deleteCompletedMessage);
|
||||
}
|
||||
} 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(getCertificatePage(certificateType), new NoPageParams(), model, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles request to download the cert by writing it to the response stream
|
||||
* for download.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @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
|
||||
*/
|
||||
@RequestMapping(value = "/{certificateType}/download", method = RequestMethod.GET)
|
||||
public void download(
|
||||
@PathVariable("certificateType") final String certificateType,
|
||||
@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
|
||||
@RequestMapping(value = "/trust-chain/download-aca-cert", method = RequestMethod.GET)
|
||||
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
|
||||
*/
|
||||
@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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 = "/issued-certificates/bulk-download", method = RequestMethod.GET)
|
||||
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 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<Certificate> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flag indicating if a device-name join/alias is required for
|
||||
* displaying the table data. This will be true if displaying a cert that is
|
||||
* associated with a device.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @return true if the list criteria modifier requires aliasing the device
|
||||
* table, false otherwise.
|
||||
*/
|
||||
private boolean hasDeviceTableToJoin(final String certificateType) {
|
||||
// Trust_Chain Credential do not contain the device table to join.
|
||||
return !certificateType.equals(TRUSTCHAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 TRUSTCHAIN -> this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"CertificateAuthorityCredential");
|
||||
case IDEVIDCERTIFICATE -> this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"IDevIDCertificate");
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an uploaded file into a certificate and populates the given model
|
||||
* with error messages if parsing fails.
|
||||
*
|
||||
* @param certificateType String containing the certificate type
|
||||
* @param file the file being uploaded from the portal
|
||||
* @param messages contains any messages that will be display on the page
|
||||
* @return the parsed certificate or null if parsing failed.
|
||||
*/
|
||||
private Certificate parseCertificate(
|
||||
final String certificateType,
|
||||
final MultipartFile file,
|
||||
final PageMessages messages) {
|
||||
log.info("Received File of Size: {}", file.getSize());
|
||||
|
||||
byte[] fileBytes;
|
||||
String fileName = file.getOriginalFilename();
|
||||
|
||||
// build the certificate from the uploaded bytes
|
||||
try {
|
||||
fileBytes = file.getBytes();
|
||||
} catch (IOException ioEx) {
|
||||
final String failMessage = String.format(
|
||||
"Failed to read uploaded file (%s): ", fileName);
|
||||
log.error(failMessage, ioEx);
|
||||
messages.addError(failMessage + ioEx.getMessage());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
switch (certificateType) {
|
||||
case IDEVIDCERTIFICATE:
|
||||
return new IDevIDCertificate(fileBytes);
|
||||
case TRUSTCHAIN:
|
||||
if (CredentialHelper.isMultiPEM(new String(fileBytes, StandardCharsets.UTF_8))) {
|
||||
try (ByteArrayInputStream certInputStream = new ByteArrayInputStream(fileBytes)) {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
Collection c = cf.generateCertificates(certInputStream);
|
||||
Iterator i = c.iterator();
|
||||
while (i.hasNext()) {
|
||||
storeCertificate(
|
||||
certificateType,
|
||||
file.getOriginalFilename(),
|
||||
messages, new CertificateAuthorityCredential(
|
||||
((java.security.cert.Certificate) i.next()).getEncoded()));
|
||||
}
|
||||
|
||||
// stop the main thread from saving/storing
|
||||
return null;
|
||||
} catch (CertificateException e) {
|
||||
throw new IOException("Cannot construct X509Certificate from the input stream",
|
||||
e);
|
||||
}
|
||||
}
|
||||
return new CertificateAuthorityCredential(fileBytes);
|
||||
default:
|
||||
final String failMessage = String.format("Failed to parse uploaded file "
|
||||
+ "(%s). Invalid certificate type: %s", fileName, certificateType);
|
||||
log.error(failMessage);
|
||||
messages.addError(failMessage);
|
||||
return null;
|
||||
}
|
||||
} catch (IOException ioEx) {
|
||||
final String failMessage = String.format(
|
||||
"Failed to parse uploaded 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 pem file (%s): ", fileName);
|
||||
log.error(failMessage, dEx);
|
||||
messages.addError(failMessage + dEx.getMessage());
|
||||
return null;
|
||||
} catch (IllegalArgumentException iaEx) {
|
||||
final String failMessage = String.format(
|
||||
"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 %s ", fileName);
|
||||
log.error(failMessage, isEx);
|
||||
messages.addError(failMessage + isEx.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 messages contains any messages that will be display on the page
|
||||
* @param certificate the certificate to store
|
||||
*/
|
||||
private void storeCertificate(
|
||||
final String certificateType,
|
||||
final String fileName,
|
||||
final PageMessages messages,
|
||||
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 + "): ";
|
||||
messages.addError(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<PlatformCredential> sharedCertificates = getCertificateByBoardSN(
|
||||
certificateType,
|
||||
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 + ")";
|
||||
messages.addError(failMessage);
|
||||
log.error(failMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} /*else {
|
||||
// this is a delta, check if the holder exists.
|
||||
PlatformCredential holderPC = PlatformCredential
|
||||
.select(certificateManager)
|
||||
.bySerialNumber(platformCertificate.getHolderSerialNumber())
|
||||
.getCertificate();
|
||||
if (holderPC == null) {
|
||||
final String failMessage = "Storing certificate failed: "
|
||||
+ "delta credential"
|
||||
+ " must have an existing holder stored. "
|
||||
+ "Credential serial "
|
||||
+ platformCertificate.getHolderSerialNumber()
|
||||
+ " doesn't exist.";
|
||||
messages.addError(failMessage);
|
||||
LOGGER.error(failMessage);
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
this.certificateRepository.save(certificate);
|
||||
parseAndSaveComponentResults(certificate);
|
||||
|
||||
final String successMsg
|
||||
= String.format("New certificate successfully uploaded (%s): ", fileName);
|
||||
messages.addSuccess(successMsg);
|
||||
log.info(successMsg);
|
||||
return;
|
||||
}
|
||||
} catch (DBServiceException dbsEx) {
|
||||
final String failMessage = String.format("Storing new certificate failed (%s): ",
|
||||
fileName);
|
||||
messages.addError(failMessage + dbsEx.getMessage());
|
||||
log.error(failMessage, dbsEx);
|
||||
return;
|
||||
} catch (IOException ioException) {
|
||||
final String ioExceptionMessage = "Failed to save component results in the database";
|
||||
messages.addError(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<ComponentResult> 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);
|
||||
messages.addSuccess(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);
|
||||
messages.addError(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);
|
||||
messages.addError(failMessage);
|
||||
log.error(failMessage);
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ 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;
|
||||
@ -25,7 +24,6 @@ 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;
|
||||
@ -44,7 +42,6 @@ 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
|
||||
@ -54,17 +51,14 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
|
||||
private static final String ENDORSEMENT_CREDENTIALS = "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;
|
||||
}
|
||||
@ -83,13 +77,20 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
return getBaseModelAndView(Page.ENDORSEMENT_KEY_CREDENTIALS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the collection of endorsement credentials that will be displayed on the endorsement
|
||||
* credentials page.
|
||||
*
|
||||
* @param input data table input received from the front-end
|
||||
* @return data table of endorsement credentials
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/list",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public DataTableResponse<EndorsementCredential> getEndorsementCredentialsTableData(
|
||||
final DataTableInput input) {
|
||||
|
||||
log.debug("Handling list request: {}", input);
|
||||
log.debug("Handling list request for endorsement credentials: {}", input);
|
||||
|
||||
// attempt to get the column property based on the order index.
|
||||
String orderColumnName = input.getOrderColumnName();
|
||||
@ -148,7 +149,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(id);
|
||||
Certificate certificate = certificateRepository.getCertificate(uuid);
|
||||
Certificate certificate = this.certificateService.findCertificate(uuid);
|
||||
|
||||
if (certificate == null) {
|
||||
// Use the term "record" here to avoid user confusion b/t cert and cred
|
||||
@ -156,20 +157,18 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
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\"";
|
||||
} 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");
|
||||
// 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());
|
||||
}
|
||||
// write cert to output stream
|
||||
response.getOutputStream().write(certificate.getRawBytes());
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String uuidError = "Failed to parse ID from: " + id;
|
||||
@ -180,7 +179,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles request to download the certs by writing it to the response stream
|
||||
* Handles request to download the endorsement credentials by writing it to the response stream
|
||||
* for download in bulk.
|
||||
*
|
||||
* @param response the response object (needed to update the header with the
|
||||
@ -188,29 +187,20 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
* @throws IOException when writing to response output stream
|
||||
*/
|
||||
@GetMapping("/bulk-download")
|
||||
public void endorsementCredentialBulkDownload(final HttpServletResponse response)
|
||||
public void bulkDownloadEndorsementCredentials(final HttpServletResponse response)
|
||||
throws IOException {
|
||||
log.info("Handling request to download all endorsement credentials");
|
||||
|
||||
final 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())) {
|
||||
|
||||
// find all the uploaded endorsement credentials
|
||||
List<Certificate> certificates = this.certificateRepository.findByType("EndorsementCredential");
|
||||
|
||||
// convert the list of certificates to a list of endorsement credentials
|
||||
List<EndorsementCredential> uploadedEKs = certificates.stream()
|
||||
.filter(eachPC -> eachPC instanceof EndorsementCredential)
|
||||
.map(eachPC -> (EndorsementCredential) eachPC).toList();
|
||||
|
||||
// get all files
|
||||
bulkDownloadEndorsementCredentials(zipOut, uploadedEKs);
|
||||
// write cert to output stream
|
||||
// write endorsement credentials to output stream and bulk download them
|
||||
this.certificateService.bulkDownloadCertificates(zipOut, ENDORSEMENT_CREDENTIALS, singleFileName);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String uuidError = "Failed to parse ID from: ";
|
||||
log.error(uuidError, ex);
|
||||
@ -220,7 +210,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload and processes an endorsement credential.
|
||||
* Uploads and processes an endorsement credential.
|
||||
*
|
||||
* @param files the files to process
|
||||
* @param attr the redirection attributes
|
||||
@ -228,7 +218,7 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
* @throws URISyntaxException if malformed URI
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
protected RedirectView upload(
|
||||
protected RedirectView uploadEndorsementCredential(
|
||||
@RequestParam("file") final MultipartFile[] files,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
|
||||
@ -242,14 +232,14 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
//Parse endorsement credential
|
||||
EndorsementCredential parseEndorsementCredential = parseEndorsementCredential(file, messages);
|
||||
EndorsementCredential parsedEndorsementCredential = parseEndorsementCredential(file, messages);
|
||||
|
||||
//Store only if it was parsed
|
||||
if (parseEndorsementCredential != null) {
|
||||
if (parsedEndorsementCredential != null) {
|
||||
certificateService.storeCertificate(
|
||||
ENDORSEMENT_CREDENTIALS,
|
||||
file.getOriginalFilename(),
|
||||
successMessages, errorMessages, parseEndorsementCredential);
|
||||
successMessages, errorMessages, parsedEndorsementCredential);
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,16 +250,16 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
}
|
||||
|
||||
/**
|
||||
* Archives (soft delete) the endorsement credential.
|
||||
* Archives (soft deletes) the endorsement credential.
|
||||
*
|
||||
* @param id the UUID of the endorsement cert to delete
|
||||
* @param id the UUID of the endorsement certificate 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(
|
||||
public RedirectView deleteEndorsementCredential(
|
||||
@RequestParam final String id,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
log.info("Handling request to delete endorsement credential id {}", id);
|
||||
@ -366,33 +356,4 @@ public class EndorsementCredentialPageController extends PageController<NoPagePa
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that packages a collection of endorsement credentials into a zip file.
|
||||
*
|
||||
* @param zipOut zip outputs stream
|
||||
* @param endorsementCredentials collection of endorsement credentials
|
||||
* @throws IOException if there are any issues packaging or downloading the zip file
|
||||
*/
|
||||
private void bulkDownloadEndorsementCredentials(final ZipOutputStream zipOut,
|
||||
final List<EndorsementCredential> endorsementCredentials)
|
||||
throws IOException {
|
||||
String zipFileName;
|
||||
final String singleFileName = "Endorsement_Certificates";
|
||||
|
||||
// get all endorsement credentials
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,119 +1,359 @@
|
||||
//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<NoPageParams> {
|
||||
//
|
||||
// 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<IDevIDCertificate> 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<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
|
||||
//
|
||||
// int currentPage = input.getStart() / input.getLength();
|
||||
// Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
//
|
||||
// FilteredRecordsList<IDevIDCertificate> records = new FilteredRecordsList<>();
|
||||
// org.springframework.data.domain.Page<IDevIDCertificate> 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<String> findSearchableColumnsNames(List<Column> 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());
|
||||
// }
|
||||
//}
|
||||
package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
import hirs.attestationca.persist.DBManagerException;
|
||||
import hirs.attestationca.persist.FilteredRecordsList;
|
||||
import hirs.attestationca.persist.entity.manager.IDevIDCertificateRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
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.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.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.ZipOutputStream;
|
||||
|
||||
@Log4j2
|
||||
@Controller
|
||||
@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/idevid-certificates")
|
||||
public class IDevIdCertificatePageController extends PageController<NoPageParams> {
|
||||
|
||||
private static final String IDEVID_CERTIFICATE = "idevid-certificates";
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the collection of idevid certificates that will be displayed on the idevid certificates
|
||||
* page.
|
||||
*
|
||||
* @param input data table input received from the front-end
|
||||
* @return data table of idevid certificates
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/list",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public DataTableResponse<IDevIDCertificate> getIDevIdCertificatesTableData(
|
||||
final DataTableInput input) {
|
||||
|
||||
log.debug("Handling list request for idevid 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<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
|
||||
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
|
||||
FilteredRecordsList<IDevIDCertificate> records = new FilteredRecordsList<>();
|
||||
org.springframework.data.domain.Page<IDevIDCertificate> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles request to download the IDevId certificate 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 downloadSingleIDevIdCertificate(
|
||||
@RequestParam final String id,
|
||||
final HttpServletResponse response)
|
||||
throws IOException {
|
||||
log.info("Handling request to download idevid certificate id {}", id);
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(id);
|
||||
Certificate certificate = this.certificateService.findCertificate(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 IDevIDCertificate uploadedIDevIdCertificate) {
|
||||
String fileName = "filename=\"" + IDevIDCertificate.class.getSimpleName()
|
||||
+ "_"
|
||||
+ uploadedIDevIdCertificate.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 IDevID Certificates 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 bulkDownloadIDevIdCertificates(final HttpServletResponse response)
|
||||
throws IOException {
|
||||
log.info("Handling request to download all idevid certificates");
|
||||
|
||||
final String fileName = "idevid_certificates.zip";
|
||||
final String singleFileName = "IDevID_Certificates";
|
||||
|
||||
// Set filename for download.
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
|
||||
response.setContentType("application/zip");
|
||||
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
|
||||
// write idevid certificates to output stream and bulk download them
|
||||
this.certificateService.bulkDownloadCertificates(zipOut, IDEVID_CERTIFICATE, singleFileName);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads and processes an idevid certificate.
|
||||
*
|
||||
* @param files the files to process
|
||||
* @param attr the redirection attributes
|
||||
* @return the redirection view
|
||||
* @throws URISyntaxException if malformed URI
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
protected RedirectView uploadIDevIdCertificate(
|
||||
@RequestParam("file") final MultipartFile[] files,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
|
||||
log.info("Handling request to upload one or more idevid certificates");
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
List<String> successMessages = new ArrayList<>();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
//Parse IDevId Certificate
|
||||
IDevIDCertificate parsedIDevIDCertificate =
|
||||
parseIDevIDCertificate(file, messages);
|
||||
|
||||
//Store only if it was parsed
|
||||
if (parsedIDevIDCertificate != null) {
|
||||
certificateService.storeCertificate(
|
||||
IDEVID_CERTIFICATE,
|
||||
file.getOriginalFilename(),
|
||||
successMessages, errorMessages, parsedIDevIDCertificate);
|
||||
}
|
||||
}
|
||||
|
||||
//Add messages to the model
|
||||
model.put(MESSAGES_ATTRIBUTE, messages);
|
||||
|
||||
return redirectTo(Page.IDEVID_CERTIFICATES, new NoPageParams(), model, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Archives (soft deletes) the idevid certificate.
|
||||
*
|
||||
* @param id the UUID of the idevid certificate 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 deleteIdevIdCertificate(
|
||||
@RequestParam final String id,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
log.info("Handling request to delete idevid id {}", id);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
|
||||
try {
|
||||
List<String> successMessages = new ArrayList<>();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
|
||||
UUID uuid = UUID.fromString(id);
|
||||
|
||||
this.certificateService.deleteCertificate(uuid, IDEVID_CERTIFICATE,
|
||||
successMessages, errorMessages);
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String uuidError = "Failed to parse ID from idevid certificate: " + id;
|
||||
messages.addError(uuidError);
|
||||
log.error(uuidError, ex);
|
||||
} catch (DBManagerException ex) {
|
||||
String dbError = "Failed to archive idevid certificate: " + id;
|
||||
messages.addError(dbError);
|
||||
log.error(dbError, ex);
|
||||
}
|
||||
|
||||
model.put(MESSAGES_ATTRIBUTE, messages);
|
||||
return redirectTo(Page.IDEVID_CERTIFICATES, new NoPageParams(), model, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that returns a list of column names that are searchable.
|
||||
*
|
||||
* @return searchable column names
|
||||
*/
|
||||
private List<String> findSearchableColumnsNames(List<Column> 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 IDevId Certificate.
|
||||
*
|
||||
* @param file file
|
||||
* @param messages page messages
|
||||
* @return IDevId certificate
|
||||
*/
|
||||
private IDevIDCertificate parseIDevIDCertificate(MultipartFile file,
|
||||
PageMessages messages) {
|
||||
log.info("Received IDevId 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 IDevId certificate file (%s): ", fileName);
|
||||
log.error(failMessage, ioEx);
|
||||
messages.addError(failMessage + ioEx.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
// attempt to build the IDevId certificate from the uploaded bytes
|
||||
try {
|
||||
return new IDevIDCertificate(fileBytes);
|
||||
} catch (IOException ioEx) {
|
||||
final String failMessage = String.format(
|
||||
"Failed to parse uploaded IDevId 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 IDevId certificate pem file (%s): ", fileName);
|
||||
log.error(failMessage, dEx);
|
||||
messages.addError(failMessage + dEx.getMessage());
|
||||
return null;
|
||||
} catch (IllegalArgumentException iaEx) {
|
||||
final String failMessage = String.format(
|
||||
"IDevId 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 IDevId certificate %s ", fileName);
|
||||
log.error(failMessage, isEx);
|
||||
messages.addError(failMessage + isEx.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
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.IssuedCertificateRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate;
|
||||
@ -11,6 +11,7 @@ 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;
|
||||
@ -22,18 +23,23 @@ 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.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
|
||||
@ -41,23 +47,22 @@ import java.util.zip.ZipOutputStream;
|
||||
@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/issued-certificates")
|
||||
public class IssuedCertificateController extends PageController<NoPageParams> {
|
||||
|
||||
private final CertificateRepository certificateRepository;
|
||||
private static final String ISSUED_CERTIFICATES = "issued-certificates";
|
||||
|
||||
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.
|
||||
* Returns the path for the view and the data model for the Issued Attestation Certificate page.
|
||||
*
|
||||
* @param params The object to map url parameters into.
|
||||
* @param model The data model for the request. Can contain data from
|
||||
@ -70,6 +75,10 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
|
||||
return getBaseModelAndView(Page.ISSUED_CERTIFICATES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/list",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ -117,16 +126,16 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles request to download the cert by writing it to the response stream
|
||||
* Handles request to download the issued attestation certificate by writing it to the response stream
|
||||
* for download.
|
||||
*
|
||||
* @param id the UUID of the cert to download
|
||||
* @param id the UUID of the issued attestation certificate 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 issuedCertificateSingleDownload(
|
||||
public void downloadSingleIssuedCertificate(
|
||||
@RequestParam final String id,
|
||||
final HttpServletResponse response)
|
||||
throws IOException {
|
||||
@ -134,17 +143,18 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(id);
|
||||
Certificate certificate = certificateRepository.getCertificate(uuid);
|
||||
Certificate certificate = this.certificateService.findCertificate(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 {
|
||||
} else if (certificate instanceof IssuedAttestationCertificate uploadedIssuedCertificate) {
|
||||
String fileName = "filename=\"" + IssuedAttestationCertificate.class.getSimpleName()
|
||||
+ "_"
|
||||
+ certificate.getSerialNumber()
|
||||
+ uploadedIssuedCertificate.getSerialNumber()
|
||||
+ ".cer\"";
|
||||
|
||||
// Set filename for download.
|
||||
@ -163,7 +173,7 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles request to download the certs by writing it to the response stream
|
||||
* Handles request to download the issued attestation certificates by writing it to the response stream
|
||||
* for download in bulk.
|
||||
*
|
||||
* @param response the response object (needed to update the header with the
|
||||
@ -171,30 +181,68 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
|
||||
* @throws IOException when writing to response output stream
|
||||
*/
|
||||
@GetMapping("/bulk-download")
|
||||
public void icBulkDownload(final HttpServletResponse response)
|
||||
public void bulkDownloadIssuedCertificates(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;
|
||||
final String fileName = "issued_certificates.zip";
|
||||
|
||||
// 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);
|
||||
// write issued attestation certificates to output stream and bulk download them
|
||||
this.certificateService.bulkDownloadCertificates(zipOut, ISSUED_CERTIFICATES, singleFileName);
|
||||
} catch (Exception ex) {
|
||||
log.error("Failed to bulk download issued certificates:", ex);
|
||||
// send a 404 error when invalid certificate
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Archives (soft deletes) the issued attestation certificate.
|
||||
*
|
||||
* @param id the UUID of the issued attestation certificate 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 deleteIssuedCertificate(
|
||||
@RequestParam final String id,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
log.info("Handling request to delete issued attestation certificate id {}", id);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
|
||||
try {
|
||||
List<String> successMessages = new ArrayList<>();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
|
||||
UUID uuid = UUID.fromString(id);
|
||||
|
||||
this.certificateService.deleteCertificate(uuid, ISSUED_CERTIFICATES,
|
||||
successMessages, errorMessages);
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String uuidError = "Failed to parse ID from issued attestation certificate: " + id;
|
||||
messages.addError(uuidError);
|
||||
log.error(uuidError, ex);
|
||||
} catch (DBManagerException ex) {
|
||||
String dbError = "Failed to archive issued attestation certificate: " + id;
|
||||
messages.addError(dbError);
|
||||
log.error(dbError, ex);
|
||||
}
|
||||
|
||||
model.put(MESSAGES_ATTRIBUTE, messages);
|
||||
return redirectTo(Page.ISSUED_CERTIFICATES, new NoPageParams(), model, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that returns a list of column names that are searchable.
|
||||
*
|
||||
@ -206,35 +254,4 @@ public class IssuedCertificateController extends PageController<NoPageParams> {
|
||||
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<Certificate> 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;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ 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;
|
||||
@ -27,7 +26,6 @@ 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;
|
||||
@ -46,7 +44,6 @@ 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
|
||||
@ -56,18 +53,16 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
|
||||
private static final String PLATFORM_CREDENTIALS = "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) {
|
||||
public PlatformCredentialPageController(
|
||||
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;
|
||||
@ -87,6 +82,13 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
return getBaseModelAndView(Page.PLATFORM_CREDENTIALS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the collection of platform credentials that will be displayed on the platform
|
||||
* credentials page.
|
||||
*
|
||||
* @param input data table input received from the front-end
|
||||
* @return data table of platform credentials
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/list",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ -154,10 +156,10 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles request to download the cert by writing it to the response stream
|
||||
* Handles request to download the platform credential by writing it to the response stream
|
||||
* for download.
|
||||
*
|
||||
* @param id the UUID of the cert to download
|
||||
* @param id the UUID of the platform credential to download
|
||||
* @param response the response object (needed to update the header with the
|
||||
* file name)
|
||||
* @throws IOException when writing to response output stream
|
||||
@ -171,7 +173,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(id);
|
||||
Certificate certificate = certificateRepository.getCertificate(uuid);
|
||||
Certificate certificate = this.certificateService.findCertificate(uuid);
|
||||
|
||||
if (certificate == null) {
|
||||
log.warn("Unable to locate platform credential record with ID: {}", uuid);
|
||||
@ -189,7 +191,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
response.setHeader("Content-Disposition", "attachment;" + fileName);
|
||||
response.setContentType("application/octet-stream");
|
||||
|
||||
// write cert to output stream
|
||||
// write platform credential to output stream
|
||||
response.getOutputStream().write(certificate.getRawBytes());
|
||||
}
|
||||
}
|
||||
@ -201,7 +203,7 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles request to download the platform crednetials by writing it to the response stream
|
||||
* Handles request to download the platform credentials by writing it to the response stream
|
||||
* for download in bulk.
|
||||
*
|
||||
* @param response the response object (needed to update the header with the
|
||||
@ -214,23 +216,15 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
log.info("Handling request to download all platform credentials");
|
||||
|
||||
final String fileName = "platform_certificates.zip";
|
||||
final String singleFileName = "Platform_Certificate";
|
||||
|
||||
// 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 credentials
|
||||
List<Certificate> certificates = this.certificateRepository.findByType("PlatformCredential");
|
||||
|
||||
// convert the list of certificates to a list of platform credentials
|
||||
List<PlatformCredential> 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);
|
||||
// write platform credentials to output stream and bulk download them
|
||||
this.certificateService.bulkDownloadCertificates(zipOut, PLATFORM_CREDENTIALS, singleFileName);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String uuidError = "Failed to parse platform credential ID from: ";
|
||||
log.error(uuidError, ex);
|
||||
@ -283,14 +277,14 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
/**
|
||||
* Archives (soft delete) the platform credential.
|
||||
*
|
||||
* @param id the UUID of the platform cert to delete
|
||||
* @param id the UUID of the platform credential 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(
|
||||
public RedirectView deletePlatformCredential(
|
||||
@RequestParam final String id,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
log.info("Handling request to delete platform credential id {}", id);
|
||||
@ -308,11 +302,11 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
successMessages, errorMessages);
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
String uuidError = "Failed to parse ID from: " + id;
|
||||
String uuidError = "Failed to parse platform credential ID from: " + id;
|
||||
messages.addError(uuidError);
|
||||
log.error(uuidError, ex);
|
||||
} catch (DBManagerException ex) {
|
||||
String dbError = "Failed to archive cert: " + id;
|
||||
String dbError = "Failed to archive platform credential: " + id;
|
||||
messages.addError(dbError);
|
||||
log.error(dbError, ex);
|
||||
}
|
||||
@ -385,33 +379,4 @@ public class PlatformCredentialPageController extends PageController<NoPageParam
|
||||
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 credentials
|
||||
* @throws IOException if there are any issues packaging or downloading the zip file
|
||||
*/
|
||||
private void bulkDownloadPlatformCertificates(final ZipOutputStream zipOut,
|
||||
final List<PlatformCredential> 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();
|
||||
}
|
||||
}
|
||||
|
@ -1,286 +1,445 @@
|
||||
//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<NoPageParams> {
|
||||
//
|
||||
// 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<String, String>(CertificateStringMapBuilder.getCertificateAuthorityInformation(
|
||||
// certificateAuthorityCredential, this.certificateRepository,
|
||||
// this.caCredentialRepository)));
|
||||
//
|
||||
// return mav;
|
||||
// }
|
||||
//
|
||||
// @ResponseBody
|
||||
// @GetMapping(value = "/list",
|
||||
// produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
// public DataTableResponse<CertificateAuthorityCredential> 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<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
|
||||
//
|
||||
// int currentPage = input.getStart() / input.getLength();
|
||||
// Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
//
|
||||
// FilteredRecordsList<CertificateAuthorityCredential> records = new FilteredRecordsList<>();
|
||||
//
|
||||
// org.springframework.data.domain.Page<CertificateAuthorityCredential> 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<String> findSearchableColumnsNames(List<Column> 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<Certificate> 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;
|
||||
// }
|
||||
//}
|
||||
package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
import hirs.attestationca.persist.DBManagerException;
|
||||
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.persist.util.CredentialHelper;
|
||||
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 hirs.attestationca.portal.page.utils.CertificateStringMapBuilder;
|
||||
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.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.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
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.ZipOutputStream;
|
||||
|
||||
@Log4j2
|
||||
@Controller
|
||||
@RequestMapping("/HIRS_AttestationCAPortal/portal/certificate-request/trust-chain")
|
||||
public class TrustChainCertificatePageController extends PageController<NoPageParams> {
|
||||
|
||||
/**
|
||||
* Model attribute name used by initPage for the aca cert info.
|
||||
*/
|
||||
static final String ACA_CERT_DATA = "acaCertData";
|
||||
|
||||
private static final String TRUST_CHAIN = "trust-chain";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/list",
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public DataTableResponse<CertificateAuthorityCredential> getTrustChainCertificatesTableData(
|
||||
final DataTableInput input) {
|
||||
log.debug("Handling list request for trust chain 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<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
|
||||
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
|
||||
FilteredRecordsList<CertificateAuthorityCredential> records = new FilteredRecordsList<>();
|
||||
|
||||
org.springframework.data.domain.Page<CertificateAuthorityCredential> 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 trust chain certificate by writing it to the response stream
|
||||
* for download.
|
||||
*
|
||||
* @param id the UUID of the trust chain certificate 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 downloadSingleTrustChainCertificate(
|
||||
@RequestParam final String id,
|
||||
final HttpServletResponse response)
|
||||
throws IOException {
|
||||
log.info("Handling request to download {}", id);
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(id);
|
||||
Certificate certificate = this.certificateService.findCertificate(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=\"" + CertificateAuthorityCredential.class.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 bulkDownloadTrustChainCertificates(final HttpServletResponse response)
|
||||
throws IOException {
|
||||
log.info("Handling request to download all trust chain certificates");
|
||||
final 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())) {
|
||||
// write trust chain certificates to output stream and bulk download them
|
||||
this.certificateService.bulkDownloadCertificates(zipOut, TRUST_CHAIN, singleFileName);
|
||||
} catch (Exception ex) {
|
||||
log.error("Failed to bulk download trust chain certificates: ", ex);
|
||||
// send a 404 error when invalid certificate
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads and processes a trust chain certificate.
|
||||
*
|
||||
* @param files the files to process
|
||||
* @param attr the redirection attributes
|
||||
* @return the redirection view
|
||||
* @throws URISyntaxException if malformed URI
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
protected RedirectView uploadTrustChainCertificate(
|
||||
@RequestParam("file") final MultipartFile[] files,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
|
||||
log.info("Handling request to upload one or more trust chain certificates");
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
List<String> successMessages = new ArrayList<>();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
//Parse trust chain certificate
|
||||
CertificateAuthorityCredential parsedTrustChainCertificate =
|
||||
parseTrustChainCertificate(file, messages);
|
||||
|
||||
//Store only if it was parsed
|
||||
if (parsedTrustChainCertificate != null) {
|
||||
certificateService.storeCertificate(
|
||||
TRUST_CHAIN,
|
||||
file.getOriginalFilename(),
|
||||
successMessages, errorMessages, parsedTrustChainCertificate);
|
||||
}
|
||||
|
||||
var a = successMessages;
|
||||
var b = errorMessages;
|
||||
}
|
||||
|
||||
//Add messages to the model
|
||||
model.put(MESSAGES_ATTRIBUTE, messages);
|
||||
|
||||
return redirectTo(Page.TRUST_CHAIN, new NoPageParams(), model, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Archives (soft deletes) the trust chain certificate.
|
||||
*
|
||||
* @param id the UUID of the trust chain certificate 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 deleteTrustChainCertificates(
|
||||
@RequestParam final String id,
|
||||
final RedirectAttributes attr) throws URISyntaxException {
|
||||
log.info("Handling request to delete trust chain certificate id {}", id);
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
|
||||
try {
|
||||
List<String> successMessages = new ArrayList<>();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
|
||||
UUID uuid = UUID.fromString(id);
|
||||
|
||||
this.certificateService.deleteCertificate(uuid, TRUST_CHAIN,
|
||||
successMessages, errorMessages);
|
||||
|
||||
var a = successMessages;
|
||||
|
||||
} 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.TRUST_CHAIN, new NoPageParams(), model, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that returns a list of column names that are searchable.
|
||||
*
|
||||
* @return searchable column names
|
||||
*/
|
||||
private List<String> findSearchableColumnsNames(List<Column> 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 trust chain certificate.
|
||||
*
|
||||
* @param file file
|
||||
* @param messages page messages
|
||||
* @return trust chain certificate
|
||||
*/
|
||||
private CertificateAuthorityCredential parseTrustChainCertificate(MultipartFile file,
|
||||
PageMessages messages) {
|
||||
log.info("Received trust chain 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 trust chain certificate file (%s): ", fileName);
|
||||
log.error(failMessage, ioEx);
|
||||
messages.addError(failMessage + ioEx.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
// attempt to build the trust chain certificates from the uploaded bytes
|
||||
try {
|
||||
if (CredentialHelper.isMultiPEM(new String(fileBytes, StandardCharsets.UTF_8))) {
|
||||
try (ByteArrayInputStream certInputStream = new ByteArrayInputStream(fileBytes)) {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
Collection<? extends java.security.cert.Certificate> c =
|
||||
cf.generateCertificates(certInputStream);
|
||||
|
||||
for (java.security.cert.Certificate certificate : c) {
|
||||
List<String> successMessages = new ArrayList<>();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
this.certificateService.storeCertificate(
|
||||
TRUST_CHAIN,
|
||||
file.getOriginalFilename(),
|
||||
successMessages,
|
||||
errorMessages,
|
||||
new CertificateAuthorityCredential(
|
||||
certificate.getEncoded()));
|
||||
}
|
||||
|
||||
// stop the main thread from saving/storing
|
||||
return null;
|
||||
} catch (CertificateException e) {
|
||||
throw new IOException("Cannot construct X509Certificate from the input stream",
|
||||
e);
|
||||
}
|
||||
}
|
||||
return new CertificateAuthorityCredential(fileBytes);
|
||||
} catch (IOException ioEx) {
|
||||
final String failMessage = String.format(
|
||||
"Failed to parse uploaded trust chain 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 trust chain certificate pem file (%s): ", fileName);
|
||||
log.error(failMessage, dEx);
|
||||
messages.addError(failMessage + dEx.getMessage());
|
||||
return null;
|
||||
} catch (IllegalArgumentException iaEx) {
|
||||
final String failMessage = String.format(
|
||||
"Trust chain 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 trust chain certificate %s ", fileName);
|
||||
log.error(failMessage, isEx);
|
||||
messages.addError(failMessage + isEx.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- <%@page contentType="text/html" pageEncoding="UTF-8"%>
|
||||
<%@page contentType="text/html" pageEncoding="UTF-8"%>
|
||||
|
||||
<%-- JSP TAGS --%>
|
||||
<%@taglib prefix="c" uri="jakarta.tags.core" %>
|
||||
@ -6,7 +6,7 @@
|
||||
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
|
||||
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
|
||||
|
||||
<%-- CONTENT --%> -->
|
||||
<%-- CONTENT --%>
|
||||
<my:page>
|
||||
<jsp:attribute name="script">
|
||||
<script
|
||||
@ -35,7 +35,7 @@
|
||||
<a
|
||||
href="${portal}/certificate-request/endorsement-key-credentials/bulk-download"
|
||||
>
|
||||
<!-- <img src="${icons}/ic_file_download_black_24dp.png" title="Download All Endorsement Certificates"> -->
|
||||
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Endorsement Certificates">
|
||||
</a>
|
||||
</form:form>
|
||||
</div>
|
||||
|
@ -7,7 +7,6 @@
|
||||
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
|
||||
|
||||
<%-- CONTENT --%>
|
||||
|
||||
<my:page>
|
||||
<jsp:attribute name="script">
|
||||
<script
|
||||
|
@ -5,7 +5,6 @@
|
||||
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
|
||||
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
|
||||
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
|
||||
|
||||
<%-- CONTENT --%>
|
||||
<my:page>
|
||||
<jsp:attribute name="script">
|
||||
@ -87,7 +86,7 @@
|
||||
</my:details-viewer>
|
||||
|
||||
<a href="${portal}/certificate-request/trust-chain/download-aca-cert">
|
||||
<!-- <img src="${baseURL}/images/icons/ic_file_download_black_24dp.png" title="Download ACA Certificate"> -->
|
||||
<img src="${baseURL}/images/icons/ic_file_download_black_24dp.png" title="Download ACA Certificate">
|
||||
</a>
|
||||
<div class="aca-input-box-header">
|
||||
<form:form
|
||||
@ -100,7 +99,7 @@
|
||||
<input id="importFile" type="file" name="file" multiple="multiple" />
|
||||
</my:file-chooser>
|
||||
<a href="${portal}/certificate-request/trust-chain/bulk-download">
|
||||
<!-- <img src="${icons}/ic_file_download_black_24dp.png" title="Download All Trust Chain Certificates"> -->
|
||||
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Trust Chain Certificates">
|
||||
</a>
|
||||
</form:form>
|
||||
</div>
|
||||
|
@ -162,21 +162,16 @@ public abstract class PageControllerTest {
|
||||
throw new IOException("Could not resolve path URI", e);
|
||||
}
|
||||
|
||||
switch (certificateClass.getSimpleName()) {
|
||||
case "EndorsementCredential":
|
||||
return new EndorsementCredential(fPath);
|
||||
case "PlatformCredential":
|
||||
return new PlatformCredential(fPath);
|
||||
case "CertificateAuthorityCredential":
|
||||
return new CertificateAuthorityCredential(fPath);
|
||||
case "IssuedAttestationCertificate":
|
||||
return new IssuedAttestationCertificate(fPath,
|
||||
endorsementCredential, platformCredentials, false);
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unknown certificate class %s", certificateClass.getName())
|
||||
);
|
||||
}
|
||||
return switch (certificateClass.getSimpleName()) {
|
||||
case "EndorsementCredential" -> new EndorsementCredential(fPath);
|
||||
case "PlatformCredential" -> new PlatformCredential(fPath);
|
||||
case "CertificateAuthorityCredential" -> new CertificateAuthorityCredential(fPath);
|
||||
case "IssuedAttestationCertificate" -> new IssuedAttestationCertificate(fPath,
|
||||
endorsementCredential, platformCredentials, false);
|
||||
default -> throw new IllegalArgumentException(
|
||||
String.format("Unknown certificate class %s", certificateClass.getName())
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,5 @@
|
||||
package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
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;
|
||||
@ -37,16 +36,16 @@ public class EndorsementKeyCredentialsPageControllerTest extends PageControllerT
|
||||
private static final String EKCERT = "certificates/fakeIntelIntermediateCA.pem";
|
||||
private static final String BADEKCERT = "certificates/badCert.pem";
|
||||
// Base path for the page
|
||||
private String pagePath;
|
||||
// Repository manager to handle data access between certificate entity and data storage in db
|
||||
@Autowired
|
||||
private CertificateRepository certificateRepository;
|
||||
private final String pagePath;
|
||||
|
||||
// Repository manager to handle data access between endorsement certificate entity and data storage in db
|
||||
@Autowired
|
||||
private EndorsementCredentialRepository endorsementCredentialRepository;
|
||||
|
||||
// A file that contains a cert that is not an EK Cert. Should be parsable as a general cert,
|
||||
// but should (eventually) not be stored as an EK because it isn't one.
|
||||
private MockMultipartFile nonEkCertFile;
|
||||
|
||||
// A file that is not a cert at all, and just contains garbage text.
|
||||
private MockMultipartFile badCertFile;
|
||||
|
||||
|
@ -91,9 +91,9 @@ public class TrustChainManagementPageControllerTest extends PageControllerTest {
|
||||
getMockMvc()
|
||||
.perform(MockMvcRequestBuilders.get(pagePath))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(model().attributeExists(CertificatePageController.ACA_CERT_DATA))
|
||||
.andExpect(model().attributeExists(TrustChainCertificatePageController.ACA_CERT_DATA))
|
||||
.andExpect(model().attribute(
|
||||
CertificatePageController.ACA_CERT_DATA,
|
||||
TrustChainCertificatePageController.ACA_CERT_DATA,
|
||||
hasEntry("issuer", "CN=Fake Root CA"))
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user