v3_issue_811: Got rid of newer bootstrap folder. Will integrate into HIRS in another issue. Figured out where the search issue lies. Need to figure out how to search on columns on the server side.

This commit is contained in:
ThatSilentCoder 2025-04-07 15:42:29 -04:00
parent 97204d25db
commit 17802d2b44
30 changed files with 459 additions and 26247 deletions

View File

@ -111,7 +111,7 @@ jobs:
run: |
mkdir artifacts
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
path: artifacts

View File

@ -4,13 +4,15 @@ import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredent
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface PlatformCertificateRepository extends JpaRepository<PlatformCredential, UUID> {
public interface PlatformCertificateRepository extends JpaRepository<PlatformCredential, UUID>,
JpaSpecificationExecutor<PlatformCredential> {
/**
* Query that retrieves a list of platform credentials using the provided archive flag.
@ -30,6 +32,7 @@ public interface PlatformCertificateRepository extends JpaRepository<PlatformCre
*/
Page<PlatformCredential> findByArchiveFlag(boolean archiveFlag, Pageable pageable);
/**
* Query that retrieves a list of platform credentials using the provided device id.
*

View File

@ -30,14 +30,14 @@ public class Column {
/**
* Column's name.
*
* @see http://datatables.net/reference/option/columns.name
* @see https://datatables.net/reference/option/columns.name
*/
private String name;
/**
* Flag to indicate if this column is searchable (true) or not (false).
*
* @see http://datatables.net/reference/option/columns.searchable
* @see https://datatables.net/reference/option/columns.searchable
*/
@NotNull
private boolean searchable;
@ -45,7 +45,7 @@ public class Column {
/**
* Flag to indicate if this column is orderable (true) or not (false).
*
* @see http://datatables.net/reference/option/columns.orderable
* @see https://datatables.net/reference/option/columns.orderable
*/
@NotNull
private boolean orderable;

View File

@ -24,16 +24,19 @@ import java.util.Map;
public class DataTableInput {
private static final int DEFAULT_LENGTH = 10;
/**
* Order parameter.
*/
@NotEmpty
private final List<Order> order = new ArrayList<>();
/**
* Per-column search parameter.
*/
@NotEmpty
private final List<Column> columns = new ArrayList<>();
/**
* Draw counter. This is used by DataTables to ensure that the Ajax returns from server-side
* processing requests are drawn in sequence by DataTables (Ajax requests are asynchronous and
@ -44,6 +47,7 @@ public class DataTableInput {
@Min(0)
@Setter
private int draw = 1;
/**
* Paging first record indicator. This is the start point in the current data set
* (0 index based - i.e. 0 is the first record).
@ -52,6 +56,7 @@ public class DataTableInput {
@Min(0)
@Setter
private int start = 0;
/**
* Number of records that the table can display in the current draw. It is expected that the
* number of records returned will be equal to this number,
@ -63,6 +68,7 @@ public class DataTableInput {
@Min(-1)
@Setter
private int length = DEFAULT_LENGTH;
/**
* Global search parameter.
*/

View File

@ -1,6 +1,5 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.persist.CriteriaModifier;
import hirs.attestationca.persist.DBManagerException;
import hirs.attestationca.persist.DBServiceException;
import hirs.attestationca.persist.FilteredRecordsList;
@ -21,6 +20,7 @@ import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredent
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
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;
@ -29,17 +29,17 @@ 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.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
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;
import org.hibernate.Session;
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;
@ -56,13 +56,13 @@ import org.springframework.web.servlet.view.RedirectView;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.ref.Reference;
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.Iterator;
@ -236,6 +236,33 @@ public class CertificatePageController extends PageController<NoPageParams> {
return mav;
}
/**
* @return
*/
private String buildSearchQuery(@NotNull String searchTerm, 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();
}
/**
* Queries for the list of Certificates and returns a data table response
* with the records.
@ -257,25 +284,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
String orderColumnName = input.getOrderColumnName();
log.debug("Ordering on column: {}", orderColumnName);
// check that the alert is not archived and that it is in the specified report
CriteriaModifier criteriaModifier = new CriteriaModifier() {
@Override
public void modify(final CriteriaQuery criteriaQuery) {
Session session = entityManager.unwrap(Session.class);
CriteriaBuilder cb = session.getCriteriaBuilder();
Root<Certificate> rimRoot = criteriaQuery.from(Reference.class);
criteriaQuery.select(rimRoot).distinct(true)
.where(cb.isNull(rimRoot.get(Certificate.ARCHIVE_FIELD)));
// add a device alias if this query includes the device table
// for getting the device (e.g. device name).
// use left join, since device may be null. Query will return all
// Certs of this type, whether it has a Device or not (device field may be null)
// if (hasDeviceTableToJoin(certificateType)) {
// criteria.createAlias("device", "device", JoinType.LEFT_OUTER_JOIN);
//}
}
};
//String searchQuery = buildSearchQuery(input.getSearch().getValue(), input.getColumns());
int currentPage = input.getStart() / input.getLength();
Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
@ -286,8 +295,32 @@ public class CertificatePageController extends PageController<NoPageParams> {
switch (certificateType) {
case PLATFORMCREDENTIAL -> {
FilteredRecordsList<PlatformCredential> records = new FilteredRecordsList<>();
org.springframework.data.domain.Page<PlatformCredential> pagedResult =
this.platformCertificateRepository.findByArchiveFlag(false, paging);
org.springframework.data.domain.Page<PlatformCredential> pagedResult = null;
if (StringUtils.isBlank(input.getSearch().getValue())) {
pagedResult =
this.platformCertificateRepository.findByArchiveFlag(false, paging);
} 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);
}
if (pagedResult.hasContent()) {
records.addAll(pagedResult.getContent());

View File

@ -99,7 +99,7 @@ public class DevicePageController extends PageController<NoPageParams> {
final DataTableInput input) {
log.debug("Handling request for device list");
String orderColumnName = input.getOrderColumnName();
log.info("Ordering on column: {}", orderColumnName);
log.debug("Ordering on column: {}", orderColumnName);
// get all the devices
FilteredRecordsList<Device> deviceList = new FilteredRecordsList<>();

View File

@ -112,11 +112,11 @@ public class ReferenceManifestPageController extends PageController<NoPageParams
method = RequestMethod.GET)
public DataTableResponse<ReferenceManifest> getTableData(
@Valid final DataTableInput input) {
log.debug("Handling request for summary list: " + input);
log.debug("Handling request for summary list: {}", input);
String orderColumnName = input.getOrderColumnName();
log.info("Ordering on column: " + orderColumnName);
log.info("Querying with the following dataTableInput: " + input);
log.debug("Ordering on column: {}", orderColumnName);
log.debug("Querying with the following dataTableInput: {}", input);
FilteredRecordsList<ReferenceManifest> records = new FilteredRecordsList<>();
int currentPage = input.getStart() / input.getLength();
@ -137,7 +137,7 @@ public class ReferenceManifestPageController extends PageController<NoPageParams
records.setRecordsFiltered(referenceManifestRepository.findByArchiveFlag(false).size());
log.debug("Returning list of size: " + records.size());
log.debug("Returning list of size: {}", records.size());
return new DataTableResponse<>(records, input);
}
@ -282,10 +282,11 @@ public class ReferenceManifestPageController extends PageController<NoPageParams
// send a 404 error when invalid Reference Manifest
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
StringBuilder fileName = new StringBuilder("filename=\"");
fileName.append(referenceManifest.getFileName());
// Set filename for download.
response.setHeader("Content-Disposition", "attachment;" + fileName);
response.setHeader("Content-Disposition",
"attachment;" + "filename=\"" + referenceManifest.getFileName()
// Set filename for download.
);
response.setContentType("application/octet-stream");
// write cert to output stream

View File

@ -89,10 +89,10 @@ public class RimDatabasePageController extends PageController<NoPageParams> {
method = RequestMethod.GET)
public DataTableResponse<ReferenceDigestValue> getTableData(
@Valid final DataTableInput input) {
log.info("Handling request for summary list: " + input);
log.debug("Handling request for summary list: {}", input);
String orderColumnName = input.getOrderColumnName();
log.info("Ordering on column: " + orderColumnName);
log.debug("Ordering on column: {}", orderColumnName);
// check that the alert is not archived and that it is in the specified report
CriteriaModifier criteriaModifier = new CriteriaModifier() {
@ -106,7 +106,7 @@ public class RimDatabasePageController extends PageController<NoPageParams> {
}
};
log.info("Querying with the following dataTableInput: " + input.toString());
log.debug("Querying with the following dataTableInput: {}", input);
FilteredRecordsList<ReferenceDigestValue> referenceDigestValues = new FilteredRecordsList<>();
@ -139,7 +139,7 @@ public class RimDatabasePageController extends PageController<NoPageParams> {
}
}
log.debug("Returning list of size: " + referenceDigestValues.size());
log.debug("Returning list of size: {}", referenceDigestValues.size());
return new DataTableResponse<>(referenceDigestValues, input);
}
}

View File

@ -8,123 +8,151 @@
<%-- 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">Platform Certificates</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">Platform Certificates</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="endorsementIcon" value="${icons}/ic_vpn_key_black_24dp.png" />
<div class="aca-input-box-header">
<form:form
method="POST"
action="${portal}/certificate-request/platform-credentials/upload"
enctype="multipart/form-data"
>
Platform Credentials
<my:file-chooser
id="platformCredentialEditor"
label="Import Platform Credentials"
>
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/certificate-request/platform-credentials/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Platform Certificates">
</a>
</form:form>
</div>
<br />
<div class="aca-data-table">
<table id="platformTable" 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>Board SN</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Endorsement</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="endorsementIcon" value="${icons}/ic_vpn_key_black_24dp.png"/>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/platform-credentials/upload" enctype="multipart/form-data">
Platform Credentials
<my:file-chooser id="platformCredentialEditor" label="Import Platform Credentials">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/certificate-request/platform-credentials/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Platform Certificates">
</a>
</form:form>
</div>
<br/>
<div class="aca-data-table">
<table id="platformTable" 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>Board SN</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Endorsement</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function () {
let url = pagePath + "/list";
let columns = [
{
name: "deviceName",
data: "deviceName",
orderable: true,
searchable: true,
render: function (data, type, full, meta) {
// if there is a device, display its name, otherwise
return full.deviceName;
},
},
{
name: "issuer",
data: "issuer",
orderable: true,
searchable: true,
},
{
name: "credentialType",
data: "credentialType",
render: function (data, type, full, meta) {
if (full.platformChainType !== "") {
return full.platformChainType;
} else {
return full.credentialType;
}
},
},
{
name: "manufacturer",
data: "manufacturer",
orderable: true,
searchable: true,
},
{ name: "model", data: "model", orderable: true, searchable: true },
{ name: "version", data: "version", orderable: true, searchable: true },
{ name: "platformSerial",data: "platformSerial", orderable: true, searchable: true },
{
name: "beginValidity",
data: "beginValidity",
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.beginValidity);
},
},
{
name: "endValidity",
data: "endValidity",
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
return formatCertificateDate(full.endValidity);
},
},
{
data: "id",
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
//Display endorsement credential
if (full.endorsementCredential === null) return "";
let html = "";
<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',
render: function (data, type, full, meta) {
if (full.platformChainType !== '') {
return full.platformChainType;
} else {
return full.credentialType;
}
}
},
{data: 'manufacturer'},
{data: 'model'},
{data: 'version'},
{data: 'platformSerial'},
{
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) {
//Display endorsement credential
if(full.endorsementCredential === null) return '';
let html = '';
let id = full.endorsementCredential.id;
html =
certificateDetailsLink("endorsement", id, false) + "&nbsp;";
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) {
// 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("platform", 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 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('platform', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
},
},
];
return html;
}
}
];
//Set data tables
setDataTables("#platformTable", url, columns);
});
</script>
</jsp:body>
</my:page>
//Set data tables
setDataTables("#platformTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -2,222 +2,265 @@
* Converts a byte to HEX.
*/
function byteToHexString(arr) {
let str = "";
$.each(arr, function (index, value) {
str += ('0' + (value & 0xFF).toString(16)).slice(-2) + ":";
});
return (str.substring(0, str.length - 2)).toUpperCase();
let str = "";
$.each(arr, function (index, value) {
str += ("0" + (value & 0xff).toString(16)).slice(-2) + ":";
});
return str.substring(0, str.length - 2).toUpperCase();
}
/**
Parses hex string for display.
*/
function parseHexString(hexString) {
let str = hexString.toUpperCase();
//Do not parse if there is 2 characters
if (str.length === 2) {
return str;
}
return str.match(/.{2}/g).join(':');
let str = hexString.toUpperCase();
//Do not parse if there is 2 characters
if (str.length === 2) {
return str;
}
return str.match(/.{2}/g).join(":");
}
/**
* Parses the HEX string value to display as byte hex string.
*/
function parseSerialNumber(hexString) {
let str = hexString.toUpperCase();
if (str.length % 2 !== 0) {
str = '0' + hexString;
}
//Do not parse if there is 2 characters
if (str.length === 2) {
return str;
}
//Parse and return
return newString = str.match(/.{2}/g).join(':');
let str = hexString.toUpperCase();
if (str.length % 2 !== 0) {
str = "0" + hexString;
}
//Do not parse if there is 2 characters
if (str.length === 2) {
return str;
}
//Parse and return
return (newString = str.match(/.{2}/g).join(":"));
}
/**
* Handles user request to delete a cert. Prompts user to confirm.
* Upon confirmation, submits the delete form which is required to make
* a POST call to delete the credential.
*/
* Handles user request to delete a cert. Prompts user to confirm.
* Upon confirmation, submits the delete form which is required to make
* a POST call to delete the credential.
*/
function handleDeleteRequest(id) {
if (confirm("Delete certificate?")) {
$('#deleteForm' + id).submit();
}
if (confirm("Delete certificate?")) {
$("#deleteForm" + id).submit();
}
}
/**
* Handles user request to delete a cert. Prompts user to confirm.
* Upon confirmation, submits the delete form which is required to make
* a POST call to delete the reference integrity manifest.
*/
* Handles user request to delete a cert. Prompts user to confirm.
* Upon confirmation, submits the delete form which is required to make
* a POST call to delete the reference integrity manifest.
*/
function handleRimDeleteRequest(id) {
if (confirm("Delete RIM?")) {
$('#deleteForm' + id).submit();
}
if (confirm("Delete RIM?")) {
$("#deleteForm" + id).submit();
}
}
/**
* Set the data tables using the columns definition, the ajax URL and
* the ID of the table.
* @param id of the data table
* @param url for the AJAX call
* @param columns definition of the table to render
*
*/
* Set the data tables using the columns definition, the ajax URL and
* the ID of the table.
* @param id of the data table
* @param url for the AJAX call
* @param columns definition of the table to render
*
*/
function setDataTables(id, url, columns) {
let dtable = $(id).DataTable({
processing: true,
serverSide: true,
ajax: {
url: url,
dataSrc: function (json) {
formatElementDates('.date');
return json.data;
}
},
columns: columns
});
return dtable;
return new DataTable(id,{
processing: true,
serverSide: true,
ajax: {
url: url,
dataSrc: function (json) {
formatElementDates(".date");
return json.data;
}
},
columns: columns
});
}
/**
* Create a certificate details like for the specified certificate
* type and ID with the corresponding icon.
* @param type of certificate
* @param id of the certificate
* @param sameType boolean indicating if the details is the same
* certificate type.
*/
* Create a certificate details like for the specified certificate
* type and ID with the corresponding icon.
* @param type of certificate
* @param id of the certificate
* @param sameType boolean indicating if the details is the same
* certificate type.
*/
function certificateDetailsLink(type, id, sameType) {
let href = portal + '/certificate-details?id=' + id + '&type=' + type;
let title = "";
let icon = icons;
//If the details is the same certificate type use assignment icon,
//otherwise use the icon for the certificate type.
if (sameType) {
title = "Details";
icon += '/ic_assignment_black_24dp.png';
} else {
switch (type) {
case "issued":
icon += "/ic_library_books_black_24dp.png";
title = "View Issued Attestation Certificate Details"
break;
case "platform":
icon += "/ic_important_devices_black_24dp.png";
title = "View Platform Certificate Details";
break;
case "endorsement":
icon += "/ic_vpn_key_black_24dp.png";
title = "View Endorsement Certificate Details";
break;
case "idevid":
icon += "/ic_vpn_key_black_24dp.png";
title = "View IDevID Certificate Details";
break;
}
}
let html = '<a href=' + href + '>'
+ '<img src="' + icon + '" title="' + title + '"></a>';
return html;
}
/**
* Create a RIM details like for the specified rim.
* type and ID with the corresponding icon.
* @param id of the rim
*/
function rimDetailsLink(id) {
let href = portal + '/rim-details?id=' + id;
let title = "";
let icon = icons;
let href = portal + "/certificate-details?id=" + id + "&type=" + type;
let title = "";
let icon = icons;
//If the details is the same certificate type use assignment icon,
//otherwise use the icon for the certificate type.
if (sameType) {
title = "Details";
icon += '/ic_assignment_black_24dp.png';
let html = '<a href=' + href + '>'
+ '<img src="' + icon + '" title="' + title + '"></a>';
return html;
}
/**
* Create a certificate delete link for the specified ID
* @param id of the certificate
* @param pagePath path to the link
*/
function certificateDeleteLink(id, pagePath) {
let icon = icons + '/ic_delete_black_24dp.png';
let formURL = pagePath + "/delete";
let html = '<a href="#!" onclick="handleDeleteRequest(\'' + id + '\')">'
+ '<img src="' + icon + '" title="Delete"></a>'
+ '<form id="deleteForm' + id + '" action="' + formURL + '" method="post">'
+ '<input name="id" type="hidden" value="' + id + '"></form>';
return html;
}
/**
* Create a RIM delete link for the specified ID
* @param id of the RIM
* @param pagePath path to the link
*/
function rimDeleteLink(id, pagePath) {
let icon = icons + '/ic_delete_black_24dp.png';
let formURL = pagePath + "/delete";
let html = '<a href="#!" onclick="handleRimDeleteRequest(\'' + id + '\')">'
+ '<img src="' + icon + '" title="Delete"></a>'
+ '<form id="deleteForm' + id + '" action="' + formURL + '" method="post">'
+ '<input name="id" type="hidden" value="' + id + '"></form>';
return html;
}
/**
* Create a certificate download link for the specified ID
* @param id of the certificate
* @param pagePath path to the link
*/
function certificateDownloadLink(id, pagePath) {
let icon = icons + '/ic_file_download_black_24dp.png';
let href = pagePath + '/download?id=' + id;
let html = '<a href="' + href + '">'
+ '<img src="' + icon + '" title="Download Certificate"></a>';
return html;
}
/**
* Create a rim download link for the specified ID
* @param id of the rim
* @param pagePath path to the link
*/
function rimDownloadLink(id, pagePath) {
let icon = icons + '/ic_file_download_black_24dp.png';
let href = pagePath + '/download?id=' + id;
let html = '<a href="' + href + '">'
+ '<img src="' + icon + '" title="Download Reference Integrity Manifest"></a>';
return html;
}
/**
* Formats a given date to a UTC string, or returns an indefinite icon
* @param date to format
*/
function formatCertificateDate(dateText) {
let date = +dateText; // Convert to numeric
if (date == 253402300799000) {
return 'Indefinite';
icon += "/ic_assignment_black_24dp.png";
} else {
switch (type) {
case "issued":
icon += "/ic_library_books_black_24dp.png";
title = "View Issued Attestation Certificate Details";
break;
case "platform":
icon += "/ic_important_devices_black_24dp.png";
title = "View Platform Certificate Details";
break;
case "endorsement":
icon += "/ic_vpn_key_black_24dp.png";
title = "View Endorsement Certificate Details";
break;
case "idevid":
icon += "/ic_vpn_key_black_24dp.png";
title = "View IDevID Certificate Details";
break;
}
}
let html =
"<a href=" +
href +
">" +
'<img src="' +
icon +
'" title="' +
title +
'"></a>';
return html;
}
return new Date(date).toUTCString();
}
/**
* Create a RIM details like for the specified rim.
* type and ID with the corresponding icon.
* @param id of the rim
*/
function rimDetailsLink(id) {
let href = portal + "/rim-details?id=" + id;
let title = "";
let icon = icons;
title = "Details";
icon += "/ic_assignment_black_24dp.png";
let html =
"<a href=" +
href +
">" +
'<img src="' +
icon +
'" title="' +
title +
'"></a>';
return html;
}
/**
* Create a certificate delete link for the specified ID
* @param id of the certificate
* @param pagePath path to the link
*/
function certificateDeleteLink(id, pagePath) {
let icon = icons + "/ic_delete_black_24dp.png";
let formURL = pagePath + "/delete";
let html =
'<a href="#!" onclick="handleDeleteRequest(\'' +
id +
"')\">" +
'<img src="' +
icon +
'" title="Delete"></a>' +
'<form id="deleteForm' +
id +
'" action="' +
formURL +
'" method="post">' +
'<input name="id" type="hidden" value="' +
id +
'"></form>';
return html;
}
/**
* Create a RIM delete link for the specified ID
* @param id of the RIM
* @param pagePath path to the link
*/
function rimDeleteLink(id, pagePath) {
let icon = icons + "/ic_delete_black_24dp.png";
let formURL = pagePath + "/delete";
let html =
'<a href="#!" onclick="handleRimDeleteRequest(\'' +
id +
"')\">" +
'<img src="' +
icon +
'" title="Delete"></a>' +
'<form id="deleteForm' +
id +
'" action="' +
formURL +
'" method="post">' +
'<input name="id" type="hidden" value="' +
id +
'"></form>';
return html;
}
/**
* Create a certificate download link for the specified ID
* @param id of the certificate
* @param pagePath path to the link
*/
function certificateDownloadLink(id, pagePath) {
let icon = icons + "/ic_file_download_black_24dp.png";
let href = pagePath + "/download?id=" + id;
let html =
'<a href="' +
href +
'">' +
'<img src="' +
icon +
'" title="Download Certificate"></a>';
return html;
}
/**
* Create a rim download link for the specified ID
* @param id of the rim
* @param pagePath path to the link
*/
function rimDownloadLink(id, pagePath) {
let icon = icons + "/ic_file_download_black_24dp.png";
let href = pagePath + "/download?id=" + id;
let html =
'<a href="' +
href +
'">' +
'<img src="' +
icon +
'" title="Download Reference Integrity Manifest"></a>';
return html;
}
/**
* Formats a given date to a UTC string, or returns an indefinite icon
* @param date to format
*/
function formatCertificateDate(dateText) {
let date = +dateText; // Convert to numeric
if (date == 253402300799000) {
return "Indefinite";
}
return new Date(date).toUTCString();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,325 +0,0 @@
/*!
* Bootstrap Reboot v4.6.2 (https://getbootstrap.com/)
* Copyright 2011-2022 The Bootstrap Authors
* Copyright 2011-2022 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus:not(:focus-visible) {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([class]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg {
overflow: hidden;
vertical-align: middle;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
[role="button"] {
cursor: pointer;
}
select {
word-wrap: normal;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button:not(:disabled),
[type="button"]:not(:disabled),
[type="reset"]:not(:disabled),
[type="submit"]:not(:disabled) {
cursor: pointer;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

View File

@ -1,8 +0,0 @@
/*!
* Bootstrap Reboot v4.6.2 (https://getbootstrap.com/)
* Copyright 2011-2022 The Bootstrap Authors
* Copyright 2011-2022 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long