v3_issue_811: search feature works pretty well for platform and trust chain management pages. Need to clean up a bit and it should be PR ready manana.
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:
ThatSilentCoder 2025-04-07 20:05:25 -04:00
parent 17802d2b44
commit aaef31db9d
7 changed files with 578 additions and 418 deletions

View File

@ -0,0 +1,76 @@
package hirs.attestationca.persist.service;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CertificateService {
private final EntityManager entityManager;
@Autowired
public CertificateService(EntityManager entityManager) {
this.entityManager = entityManager;
}
/**
* @param entityClass generic entity class
* @param searchableColumns list of the searchable column name
* @param searchText text that waas input in the search textbox
* @param archiveFlag
* @param pageable
* @param <T> generic entity class
* @return
*/
public <T> Page<T> findBySearchableColumnsAndArchiveFlag(Class<T> entityClass,
List<String> searchableColumns,
String searchText,
Boolean archiveFlag,
Pageable pageable) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = criteriaBuilder.createQuery(entityClass);
Root<T> certificate = query.from(entityClass);
List<Predicate> predicates = new ArrayList<>();
// Dynamically add search conditions for each field that should be searchable
if (!StringUtils.isBlank(searchText)) {
// Dynamically loop through columns and create LIKE conditions for each one
for (String columnName : searchableColumns) {
Predicate predicate =
criteriaBuilder.like(criteriaBuilder.lower(certificate.get(columnName)),
"%" + searchText.toLowerCase() + "%");
predicates.add(predicate);
}
}
Predicate likeConditions = criteriaBuilder.or(predicates.toArray(new Predicate[0]));
// Add archiveFlag condition if specified
query.where(criteriaBuilder.and(likeConditions,
criteriaBuilder.equal(certificate.get("archiveFlag"), archiveFlag)));
// Apply pagination
TypedQuery<T> typedQuery = entityManager.createQuery(query);
int totalRows = typedQuery.getResultList().size(); // Get the total count for pagination
typedQuery.setFirstResult((int) pageable.getOffset());
typedQuery.setMaxResults(pageable.getPageSize());
// Wrap the result in a Page object to return pagination info
List<T> resultList = typedQuery.getResultList();
return new PageImpl<>(resultList, pageable, totalRows);
}
}

View File

@ -22,18 +22,20 @@ public class Column {
/**
* Column's data source.
*
* @see http://datatables.net/reference/option/columns.data
* @see https://datatables.net/reference/option/columns.data
*/
@NotBlank
private String data;
/**
* Column's name.
*
* @see https://datatables.net/reference/option/columns.name
*/
@NotBlank
private String name;
/**
* Flag to indicate if this column is searchable (true) or not (false).
*
@ -55,14 +57,4 @@ public class Column {
*/
@NotNull
private Search search;
/**
* Set the search value to apply to this column.
*
* @param searchValue if any, the search value to apply
*/
public void setSearchValue(final String searchValue) {
this.search.setValue(searchValue);
}
}

View File

@ -22,6 +22,7 @@ public class Search {
*/
@NotNull
private String value = "";
/**
* true if the global filter should be treated as a regular expression for advanced searching,
* false otherwise. Note that normally server-side processing scripts will not perform regular

View File

@ -19,6 +19,7 @@ import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestati
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
import hirs.attestationca.persist.service.CertificateService;
import hirs.attestationca.persist.util.CredentialHelper;
import hirs.attestationca.portal.datatables.Column;
import hirs.attestationca.portal.datatables.DataTableInput;
@ -28,10 +29,7 @@ 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.persistence.EntityManager;
import jakarta.persistence.criteria.Predicate;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.encoders.DecoderException;
@ -39,7 +37,6 @@ 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.data.jpa.domain.Specification;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -62,7 +59,6 @@ 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.Iterator;
@ -70,6 +66,7 @@ import java.util.LinkedList;
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;
@ -98,8 +95,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
private final IssuedCertificateRepository issuedCertificateRepository;
private final CACredentialRepository caCredentialRepository;
private final IDevIDCertificateRepository iDevIDCertificateRepository;
@Autowired(required = false)
private EntityManager entityManager;
private final CertificateService certificateService;
private CertificateAuthorityCredential certificateAuthorityCredential;
/**
@ -122,6 +118,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
final IssuedCertificateRepository issuedCertificateRepository,
final CACredentialRepository caCredentialRepository,
final IDevIDCertificateRepository iDevIDCertificateRepository,
final CertificateService certificateService,
final X509Certificate acaCertificate) {
super(Page.TRUST_CHAIN);
this.certificateRepository = certificateRepository;
@ -131,6 +128,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
this.issuedCertificateRepository = issuedCertificateRepository;
this.caCredentialRepository = caCredentialRepository;
this.iDevIDCertificateRepository = iDevIDCertificateRepository;
this.certificateService = certificateService;
try {
certificateAuthorityCredential
@ -237,30 +235,16 @@ public class CertificatePageController extends PageController<NoPageParams> {
}
/**
* @return
* Helper method that returns a list of column names that are searchable.
*
* @return searchable column names
*/
private String buildSearchQuery(@NotNull String searchTerm, List<Column> columns) {
private List<String> findSearchableColumnsNames(List<Column> columns) {
StringBuilder searchQuery = new StringBuilder();
if (!StringUtils.isBlank(searchTerm)) {
// Iterate over columns and add search conditions for the ones that are searchable
boolean firstCondition = true;
for (Column column : columns) {
if (column.isSearchable()) {
if (!firstCondition) {
searchQuery.append(" OR ");
}
searchQuery.append(column.getName()).append(" LIKE '%").append(searchTerm).append("%'");
firstCondition = false;
}
}
}
return searchQuery.toString();
// grab all the columns that are searchable, then grab all of those columns names and
// create a list of those string names
return columns.stream().filter(Column::isSearchable).map(Column::getName)
.collect(Collectors.toList());
}
/**
@ -282,12 +266,14 @@ public class CertificatePageController extends PageController<NoPageParams> {
// attempt to get the column property based on the order index.
String orderColumnName = input.getOrderColumnName();
log.debug("Ordering on column: {}", orderColumnName);
//String searchQuery = buildSearchQuery(input.getSearch().getValue(), input.getColumns());
String searchText = input.getSearch().getValue();
List<String> searchableColumns = findSearchableColumnsNames(input.getColumns());
int currentPage = input.getStart() / input.getLength();
Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
Pageable pageable = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
// special parsing for platform credential
// Add the EndorsementCredential for each PlatformCredential based on the
@ -296,30 +282,18 @@ public class CertificatePageController extends PageController<NoPageParams> {
case PLATFORMCREDENTIAL -> {
FilteredRecordsList<PlatformCredential> records = new FilteredRecordsList<>();
org.springframework.data.domain.Page<PlatformCredential> pagedResult = null;
org.springframework.data.domain.Page<PlatformCredential> pagedResult;
if (StringUtils.isBlank(input.getSearch().getValue())) {
if (StringUtils.isBlank(searchText)) {
pagedResult =
this.platformCertificateRepository.findByArchiveFlag(false, paging);
this.platformCertificateRepository.findByArchiveFlag(false, pageable);
} else {
Specification<PlatformCredential> spec = (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
for (Column column : input.getColumns()) {
if (column.isSearchable()) {
predicates.add(criteriaBuilder.like(
criteriaBuilder.lower(root.get(column.getName())),
"%" + column.getSearch().getValue().toLowerCase() + "%"
));
}
}
// Combine predicates into a single query (AND all conditions)
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
pagedResult = platformCertificateRepository.findAll(spec, paging);
pagedResult =
this.certificateService.findBySearchableColumnsAndArchiveFlag(
PlatformCredential.class,
searchableColumns,
searchText,
false, pageable);
}
if (pagedResult.hasContent()) {
@ -353,8 +327,19 @@ public class CertificatePageController extends PageController<NoPageParams> {
}
case ENDORSEMENTCREDENTIAL -> {
FilteredRecordsList<EndorsementCredential> records = new FilteredRecordsList<>();
org.springframework.data.domain.Page<EndorsementCredential> pagedResult =
this.endorsementCredentialRepository.findByArchiveFlag(false, paging);
org.springframework.data.domain.Page<EndorsementCredential> pagedResult;
if (StringUtils.isBlank(searchText)) {
pagedResult = this.endorsementCredentialRepository.findByArchiveFlag(false, pageable);
} else {
pagedResult =
this.certificateService.findBySearchableColumnsAndArchiveFlag(
EndorsementCredential.class,
searchableColumns,
searchText,
false, pageable);
}
if (pagedResult.hasContent()) {
records.addAll(pagedResult.getContent());
@ -370,8 +355,21 @@ public class CertificatePageController extends PageController<NoPageParams> {
}
case TRUSTCHAIN -> {
FilteredRecordsList<CertificateAuthorityCredential> records = new FilteredRecordsList<>();
org.springframework.data.domain.Page<CertificateAuthorityCredential> pagedResult =
this.caCredentialRepository.findByArchiveFlag(false, paging);
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());
@ -387,8 +385,19 @@ public class CertificatePageController extends PageController<NoPageParams> {
}
case ISSUEDCERTIFICATES -> {
FilteredRecordsList<IssuedAttestationCertificate> records = new FilteredRecordsList<>();
org.springframework.data.domain.Page<IssuedAttestationCertificate> pagedResult =
this.issuedCertificateRepository.findByArchiveFlag(false, paging);
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());
@ -403,9 +412,20 @@ public class CertificatePageController extends PageController<NoPageParams> {
return new DataTableResponse<>(records, input);
}
case IDEVIDCERTIFICATE -> {
FilteredRecordsList<IDevIDCertificate> records = new FilteredRecordsList<IDevIDCertificate>();
org.springframework.data.domain.Page<IDevIDCertificate> pagedResult =
this.iDevIDCertificateRepository.findByArchiveFlag(false, paging);
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());

View File

@ -7,94 +7,110 @@
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script
type="text/javascript"
src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"
></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle"
>Endorsement Key Credentials</jsp:attribute
>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Endorsement Key Credentials</jsp:attribute>
<jsp:body>
<div class="aca-input-box-header">
<form:form
method="POST"
action="${portal}/certificate-request/endorsement-key-credentials/upload"
enctype="multipart/form-data"
>
Import Endorsement Key Credentials
<my:file-chooser
id="ek-editor"
label="Import Endorsement Key Credentials"
>
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a
href="${portal}/certificate-request/endorsement-key-credentials/bulk"
>
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Endorsement Certificates">
</a>
</form:form>
</div>
<br />
<div class="aca-data-table">
<table id="endorsementKeyTable" class="display" width="100%">
<thead>
<tr>
<th>Device</th>
<th>Issuer</th>
<th>Type</th>
<th>Manufacturer</th>
<th>Model</th>
<th>Version</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function () {
let url = pagePath + "/list";
let columns = [
{
data: "deviceName",
searchable: true,
orderable: true,
render: function (data, type, full, meta) {
// if there's a device, display its name, otherwise
return full.deviceName;
},
},
{ data: "issuer", searchable: true, orderable: true },
{ data: "credentialType", searchable: true, orderable: true },
{ data: "manufacturer", searchable: true, orderable: true },
{ data: "model", searchable: true, orderable: true },
{ data: "version", searchable: true, orderable: true },
{
data: "beginValidity",
searchable: false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.beginValidity);
},
},
{
data: "endValidity",
searchable: false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.endValidity);
},
},
{
data: "id",
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
let html = "";
html += certificateDetailsLink("endorsement", full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
<jsp:body>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/endorsement-key-credentials/upload" enctype="multipart/form-data">
Import Endorsement Key Credentials
<my:file-chooser id="ek-editor" label="Import Endorsement Key Credentials">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/certificate-request/endorsement-key-credentials/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Endorsement Certificates">
</a>
</form:form>
</div>
<br/>
<div class="aca-data-table">
<table id="endorsementKeyTable" class="display" width="100%">
<thead>
<tr>
<th>Device</th>
<th>Issuer</th>
<th>Type</th>
<th>Manufacturer</th>
<th>Model</th>
<th>Version</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
let url = pagePath +'/list';
let columns = [
{
data: 'deviceName',
render: function (data, type, full, meta) {
// if there's a device, display its name, otherwise
return full.deviceName;
}
},
{data: 'issuer'},
{data: 'credentialType'},
{data: 'manufacturer'},
{data: 'model'},
{data: 'version'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.beginValidity);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
let html = '';
html += certificateDetailsLink('endorsement', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
},
},
];
return html;
}
}
];
//Set data tables
setDataTables("#endorsementKeyTable", url, columns);
});
</script>
</jsp:body>
</my:page>
//Set data tables
setDataTables("#endorsementKeyTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -7,131 +7,153 @@
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script
type="text/javascript"
src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"
></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Issued Certificates</jsp:attribute>
<jsp:body>
<div class="aca-input-box-header">
Issued Credentials
<a href="${portal}/certificate-request/issued-certificates/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Issued Certificates">
</a>
</div>
<br />
<div class="aca-data-table">
<table id="issuedTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Hostname</th>
<th rowspan="2">Type</th>
<th rowspan="2">Issuer</th>
<th rowspan="2">Valid (begin)</th>
<th rowspan="2">Valid (end)</th>
<th colspan="2">Credentials</th>
<th rowspan="2">Options</th>
</tr>
<tr>
<th>Endorsement</th>
<th>Platform</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function () {
let url = pagePath + "/list";
let columns = [
{
name: "deviceName",
data: "deviceName",
searchable: true,
orderable: true,
render: function (data, type, full, meta) {
// if there's a device, display its name, otherwise
// display nothing
return full.deviceName;
},
},
{
name: "ldevID",
data: "ldevID",
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
if (data === true) {
return "LDevID";
}
return "AK";
},
},
{
name: "issuer",
data: "issuer",
searchable: true,
orderable: true,
},
{
name: "beginValidity",
data: "beginValidity",
searchable: false,
orderable: true,
render: function (data, type, full, meta) {
return formatCertificateDate(data);
},
},
{
name: "endValidity",
data: "endValidity",
searchable: false,
orderable: true,
render: function (data, type, full, meta) {
return formatCertificateDate(data);
},
},
{
data: "id",
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
//Display endorsement credential
let html = "";
if (
full.endorsementCredential !== undefined &&
full.endorsementCredential !== null
) {
let id = full.endorsementCredential.id;
html +=
certificateDetailsLink("endorsement", id, false) + "&nbsp;";
}
return html;
},
},
{
data: "id",
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
//Display platform credential
let html = "";
if (
full.platformCredentials !== undefined &&
full.platformCredentials !== null
) {
let size = full.platformCredentials.length;
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Issued Certificates</jsp:attribute>
<jsp:body>
<div class="aca-input-box-header">
Issued Credentials
<a href="${portal}/certificate-request/issued-certificates/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Issued Certificates">
</a>
</div>
<br />
<div class="aca-data-table">
<table id="issuedTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Hostname</th>
<th rowspan="2">Type</th>
<th rowspan="2">Issuer</th>
<th rowspan="2">Valid (begin)</th>
<th rowspan="2">Valid (end)</th>
<th colspan="2">Credentials</th>
<th rowspan="2">Options</th>
</tr>
<tr>
<th>Endorsement</th>
<th>Platform</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
let url = pagePath + '/list';
let columns = [
{
data: 'deviceName',
render: function (data, type, full, meta) {
// if there's a device, display its name, otherwise
// display nothing
return full.deviceName;
}
},
{
data: 'ldevID',
searchable:false,
render: function (data, type, full, meta) {
if (data === true) {
return "LDevID";
}
return "AK";
}
},
{data: 'issuer'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(data);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(data);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display endorsement credential
let html = '';
if (full.endorsementCredential !== undefined
&& full.endorsementCredential !== null){
let id = full.endorsementCredential.id;
html += certificateDetailsLink('endorsement', id, false) +'&nbsp;';
}
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display platform credential
let html = '';
if (full.platformCredentials !== undefined
&& full.platformCredentials !== null) {
let size = full.platformCredentials.length;
for (let i = 0; i < size; i++) {
let id = full.platformCredentials[i].id;
html +=
certificateDetailsLink("platform", id, false) + "&nbsp;";
}
}
for(let i = 0; i < size; i++) {
let id = full.platformCredentials[i].id;
html += certificateDetailsLink('platform', id, false) +'&nbsp;';
}
}
return html;
},
},
{
data: "id",
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
// set up link to details page
let html = "";
html += certificateDetailsLink("issued", full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// set up link to details page
let html = '';
html += certificateDetailsLink('issued', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#issuedTable", url, columns);
});
</script>
</jsp:body>
return html;
},
},
];
//Set data tables
setDataTables("#issuedTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -8,148 +8,181 @@
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Trust Chain Management</jsp:attribute>
<jsp:attribute name="script">
<script
type="text/javascript"
src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"
></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Trust Chain Management</jsp:attribute>
<jsp:body>
<span class="aca-input-box-header">
HIRS Attestation CA Certificate
</span>
<my:details-viewer id="aca-cert-viewer" label="HIRS Attestation CA Certificate">
<div class="container-fluid">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Issuer</span></div>
<div id="issuer" class="col col-md-8">
<!-- Display the issuer, and provide a link to the issuer details if provided -->
<c:choose>
<c:when test="${not empty acaCertData.issuerID}">
<a href="${portal}/certificate-details?id=${acaCertData.issuerID}&type=certificateauthority">
${acaCertData.issuer}
</a>
</c:when>
<c:otherwise>
${acaCertData.issuer}
</c:otherwise>
</c:choose>
</div>
</div>
<c:if test="${not empty acaCertData.subject}">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Subject</span></div>
<div id="subject" class="col col-md-8">${acaCertData.subject}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Serial Number</span></div>
<div id="serialNumber" class="col col-md-8">${acaCertData.serialNumber}</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Validity</span></div>
<div id="validity" class="col col-md-8">
<div>Not Before:&nbsp;<span>${acaCertData.beginValidity}</span></div>
<div>Not After:&nbsp;<span>${acaCertData.endValidity}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Signature</span></div>
<div id="signature" class="col col-md-8"></div>
</div>
<c:if test="${not empty acaCertData.encodedPublicKey}">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Public Key</span></div>
<div id="encodedPublicKey" class="col col-md-8"></div>
</div>
</c:if>
<div class="spacer"></div>
</div>
</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">
</a>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/trust-chain/upload" enctype="multipart/form-data">
Trust Chain CA Certificates
<my:file-chooser id="tc-editor" label="Import Trust Chain Certificates">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/certificate-request/trust-chain/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Trust Chain Certificates">
<jsp:body>
<span class="aca-input-box-header"> HIRS Attestation CA Certificate </span>
<my:details-viewer
id="aca-cert-viewer"
label="HIRS Attestation CA Certificate"
>
<div class="container-fluid">
<div class="row">
<div class="col-md-2 col-md-offset-1">
<span class="colHeader">Issuer</span>
</div>
<div id="issuer" class="col col-md-8">
<!-- Display the issuer, and provide a link to the issuer details if provided -->
<c:choose>
<c:when test="${not empty acaCertData.issuerID}">
<a
href="${portal}/certificate-details?id=${acaCertData.issuerID}&type=certificateauthority"
>
${acaCertData.issuer}
</a>
</form:form>
</c:when>
<c:otherwise> ${acaCertData.issuer} </c:otherwise>
</c:choose>
</div>
</div>
<br/>
<div class="aca-data-table">
<table id="trustChainTable" class="display" width="100%">
<thead>
<tr>
<th>Issuer</th>
<th>Subject</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Options</th>
</tr>
</thead>
</table>
<c:if test="${not empty acaCertData.subject}">
<div class="row">
<div class="col-md-2 col-md-offset-1">
<span class="colHeader">Subject</span>
</div>
<div id="subject" class="col col-md-8">${acaCertData.subject}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-2 col-md-offset-1">
<span class="colHeader">Serial Number</span>
</div>
<div id="serialNumber" class="col col-md-8">
${acaCertData.serialNumber}
</div>
</div>
<script>
$(document).ready(function() {
let url = pagePath +'/list';
let signature = ${acaCertData.signature};
<div class="row">
<div class="col-md-2 col-md-offset-1">
<span class="colHeader">Validity</span>
</div>
<div id="validity" class="col col-md-8">
<div>
Not Before:&nbsp;<span>${acaCertData.beginValidity}</span>
</div>
<div>Not After:&nbsp;<span>${acaCertData.endValidity}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-1">
<span class="colHeader">Signature</span>
</div>
<div id="signature" class="col col-md-8"></div>
</div>
<c:if test="${not empty acaCertData.encodedPublicKey}">
<div class="row">
<div class="col-md-2 col-md-offset-1">
<span class="colHeader">Public Key</span>
</div>
<div id="encodedPublicKey" class="col col-md-8"></div>
</div>
</c:if>
<div class="spacer"></div>
</div>
</my:details-viewer>
//Format validity time
$("#validity span").each(function(){
$(this).text(formatCertificateDate($(this).text()));
});
<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">
</a>
<div class="aca-input-box-header">
<form:form
method="POST"
action="${portal}/certificate-request/trust-chain/upload"
enctype="multipart/form-data"
>
Trust Chain CA Certificates
<my:file-chooser id="tc-editor" label="Import Trust Chain Certificates">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/certificate-request/trust-chain/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Trust Chain Certificates">
</a>
</form:form>
</div>
<br />
<div class="aca-data-table">
<table id="trustChainTable" class="display" width="100%">
<thead>
<tr>
<th>Issuer</th>
<th>Subject</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
let url = pagePath +'/list';
let signature = ${acaCertData.signature};
//Convert byte array to string
$("#signature").html(byteToHexString(signature));
//Format validity time
$("#validity span").each(function(){
$(this).text(formatCertificateDate($(this).text()));
});
<c:if test="${not empty acaCertData.encodedPublicKey}">
//Change publick key byte to hex
let publicKey = ${acaCertData.encodedPublicKey};
$("#encodedPublicKey").html(byteToHexString(publicKey));
</c:if>
//Convert byte array to string
$("#signature").html(byteToHexString(signature));
let columns = [
{data: 'issuer'},
{data: 'subject'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.beginValidity);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
let html = '';
html += certificateDetailsLink('certificateauthority', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#trustChainTable", url, columns);
});
</script>
</jsp:body>
<c:if test="${not empty acaCertData.encodedPublicKey}">
//Change publick key byte to hex
let publicKey = ${acaCertData.encodedPublicKey};
$("#encodedPublicKey").html(byteToHexString(publicKey));
</c:if>
</my:page>
let columns = [
{
name: "issuer",
data: "issuer",
orderable: true,
searchable: true,
},
{
name: 'subject',
data: 'subject'
},
{
name: 'beginValidity',
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.beginValidity);
}
},
{
name: 'endValidity',
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
let html = '';
html += certificateDetailsLink('certificateauthority', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#trustChainTable", url, columns);
});
</script>
</jsp:body>
</my:page>