From 02cb30ad6d15ab5869ebcbc5c4b4a2a4c37709d5 Mon Sep 17 00:00:00 2001 From: apldev2 Date: Tue, 20 Nov 2018 10:21:11 -0500 Subject: [PATCH] Add changes for device deletion. Changes data structures to facilitate deletion of devices from the DB and all other entries with foreign key relationships. --- .../persist/SupplyChainValidationSummary.java | 119 ++++++++++++++++++ .../DeviceAssociatedCertificate.java | 6 + .../IssuedAttestationCertificate.java | 13 ++ .../java/hirs/persist/DBDeviceManager.java | 22 ++++ .../src/main/java/hirs/persist/DBManager.java | 2 +- .../main/java/hirs/persist/DeviceManager.java | 11 ++ .../persist/DBCertificateManagerTest.java | 44 ++++++- .../hirs/persist/DBDeviceManagerTest.java | 29 +++++ 8 files changed, 241 insertions(+), 5 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java b/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java index a32befe9..d60c618b 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/SupplyChainValidationSummary.java @@ -1,6 +1,12 @@ package hirs.data.persist; import com.google.common.base.Preconditions; +import hirs.persist.CrudManager; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; +import org.hibernate.criterion.Conjunction; +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Restrictions; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -11,10 +17,14 @@ import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.UUID; /** * A container class to group multiple related {@link SupplyChainValidation} instances @@ -26,6 +36,8 @@ public class SupplyChainValidationSummary extends ArchivableEntity { @JoinColumn(name = "device_id") private final Device device; + private static final String DEVICE_ID_FIELD = "device.id"; + @Column @Enumerated(EnumType.STRING) private final AppraisalStatus.Status overallValidationResult; @@ -43,6 +55,113 @@ public class SupplyChainValidationSummary extends ArchivableEntity { validations = Collections.emptySet(); } + /** + * This class enables the retrieval of SupplyChainValidationSummaries by their attributes. + */ + public static class Selector { + private final CrudManager + supplyChainValidationSummaryCrudManager; + + private final Map fieldValueSelections; + + /** + * Construct a new Selector that will use the given {@link CrudManager} to + * retrieve SupplyChainValidationSummaries. + * + * @param supplyChainValidationSummaryCrudManager the summary manager to be used to retrieve + * supply chain validation summaries + */ + public Selector( + final CrudManager + supplyChainValidationSummaryCrudManager) { + Preconditions.checkArgument( + supplyChainValidationSummaryCrudManager != null, + "supply chain validation summary manager cannot be null" + ); + + this.supplyChainValidationSummaryCrudManager = supplyChainValidationSummaryCrudManager; + this.fieldValueSelections = new HashMap<>(); + } + + + /** + * Construct the criterion that can be used to query for supply chain validation summaries + * matching the configuration of this Selector. + * + * @return a Criterion that can be used to query for supply chain validation summaries + * matching the configuration of this instance + */ + public Criterion getCriterion() { + Conjunction conj = new Conjunction(); + + for (Map.Entry fieldValueEntry : fieldValueSelections.entrySet()) { + conj.add(Restrictions.eq(fieldValueEntry.getKey(), fieldValueEntry.getValue())); + } + + return conj; + } + + /** + * Set a field name and value to match. + * + * @param name the field name to query + * @param value the value to query + */ + protected void setFieldValue(final String name, final Object value) { + Object valueToAssign = value; + + Preconditions.checkArgument( + value != null, + "field value cannot be null." + ); + + if (value instanceof String) { + Preconditions.checkArgument( + StringUtils.isNotEmpty((String) value), + "field value cannot be empty." + ); + } + + if (value instanceof byte[]) { + byte[] valueBytes = (byte[]) value; + + Preconditions.checkArgument( + ArrayUtils.isNotEmpty(valueBytes), + "field value cannot be empty." + ); + + valueToAssign = Arrays.copyOf(valueBytes, valueBytes.length); + } + + fieldValueSelections.put(name, valueToAssign); + } + + + /** + * Specify a device id that supply chain validation summaries must have to be considered + * as matching. + * + * @param device the device id to query + * @return this instance (for chaining further calls) + */ + public Selector byDeviceId(final UUID device) { + setFieldValue(DEVICE_ID_FIELD, device); + return this; + } + } + + /** + * Get a Selector for use in retrieving SupplyChainValidationSummary. + * + * @param certMan the CrudManager to be used to retrieve persisted supply chain validation + * summaries + * @return a SupplyChainValidationSummary.Selector instance to use for retrieving certificates + */ + public static SupplyChainValidationSummary.Selector select( + final CrudManager certMan) { + return new SupplyChainValidationSummary.Selector(certMan); + } + /** * Construct a new SupplyChainValidationSummary. * diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/certificate/DeviceAssociatedCertificate.java b/HIRS_Utils/src/main/java/hirs/data/persist/certificate/DeviceAssociatedCertificate.java index bdc3f43a..f21922bd 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/certificate/DeviceAssociatedCertificate.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/certificate/DeviceAssociatedCertificate.java @@ -21,6 +21,12 @@ public abstract class DeviceAssociatedCertificate extends Certificate { @JoinColumn(name = "device_id") private Device device; + /** + * Holds the name of the entity 'DEVICE_ID' field. + */ + protected static final String DEVICE_ID_FIELD = "device.id"; + + /** * Default Constructor. */ diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/certificate/IssuedAttestationCertificate.java b/HIRS_Utils/src/main/java/hirs/data/persist/certificate/IssuedAttestationCertificate.java index 1c383667..50e0dda5 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/certificate/IssuedAttestationCertificate.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/certificate/IssuedAttestationCertificate.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Collections; import java.util.Set; +import java.util.UUID; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.JoinColumn; @@ -40,6 +41,18 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate { public Selector(final CertificateManager certificateManager) { super(certificateManager, IssuedAttestationCertificate.class); } + + /** + * Specify a device id that certificates must have to be considered + * as matching. + * + * @param device the device id to query + * @return this instance (for chaining further calls) + */ + public Selector byDeviceId(final UUID device) { + setFieldValue(DEVICE_ID_FIELD, device); + return this; + } } /** diff --git a/HIRS_Utils/src/main/java/hirs/persist/DBDeviceManager.java b/HIRS_Utils/src/main/java/hirs/persist/DBDeviceManager.java index a8df7ffc..fbc5de9b 100644 --- a/HIRS_Utils/src/main/java/hirs/persist/DBDeviceManager.java +++ b/HIRS_Utils/src/main/java/hirs/persist/DBDeviceManager.java @@ -274,4 +274,26 @@ public class DBDeviceManager extends DBManager implements return devices; } + /** + * Deletes the Device from the database. This removes all + * of the database entries that stored information with regards to the + * Device with a foreign key relationship. + * + * @param name of the device to be deleted + * @return true if successfully found and deleted, false if otherwise + * @throws DeviceGroupManagerException + * if unable to find the device group or delete it from the + * database + */ + @Override + public final boolean deleteDevice(final String name) + throws DeviceManagerException { + LOGGER.debug("deleting device: {}", name); + try { + return super.delete(name); + } catch (DBManagerException e) { + throw new DeviceManagerException(e); + } + } + } diff --git a/HIRS_Utils/src/main/java/hirs/persist/DBManager.java b/HIRS_Utils/src/main/java/hirs/persist/DBManager.java index 549d0c6c..d7883cf5 100644 --- a/HIRS_Utils/src/main/java/hirs/persist/DBManager.java +++ b/HIRS_Utils/src/main/java/hirs/persist/DBManager.java @@ -115,7 +115,7 @@ public class DBManager extends AbstractDbManager { * @throws DBManagerException if an error is encountered while performing the query or creating * the result objects */ - protected final List getWithCriteria(final Collection criteriaCollection) + public final List getWithCriteria(final Collection criteriaCollection) throws DBManagerException { return retryTemplate.execute( new RetryCallback, DBManagerException>() { diff --git a/HIRS_Utils/src/main/java/hirs/persist/DeviceManager.java b/HIRS_Utils/src/main/java/hirs/persist/DeviceManager.java index a409d43f..a7a3b244 100644 --- a/HIRS_Utils/src/main/java/hirs/persist/DeviceManager.java +++ b/HIRS_Utils/src/main/java/hirs/persist/DeviceManager.java @@ -118,4 +118,15 @@ public interface DeviceManager extends OrderedListQuerier { */ List getDefaultDevices() throws DeviceManagerException; + /** + * Delete the Device identified by name. If + * the deletion is successful, true is returned. Otherwise, false is + * returned. + * + * @param name of the Device to delete + * @return boolean indicating outcome of the deletion + * @throws DeviceManagerException if unable to delete the device group + */ + boolean deleteDevice(String name) throws DeviceManagerException; + } diff --git a/HIRS_Utils/src/test/java/hirs/persist/DBCertificateManagerTest.java b/HIRS_Utils/src/test/java/hirs/persist/DBCertificateManagerTest.java index d6d01710..78e10a8a 100644 --- a/HIRS_Utils/src/test/java/hirs/persist/DBCertificateManagerTest.java +++ b/HIRS_Utils/src/test/java/hirs/persist/DBCertificateManagerTest.java @@ -1,12 +1,17 @@ package hirs.persist; +import hirs.data.persist.Device; +import hirs.data.persist.DeviceGroup; import hirs.data.persist.SpringPersistenceTest; import hirs.data.persist.certificate.Certificate; import hirs.data.persist.certificate.CertificateAuthorityCredential; +import hirs.data.persist.certificate.CertificateTest; import hirs.data.persist.certificate.ConformanceCredential; +import hirs.data.persist.certificate.DeviceAssociatedCertificate; import hirs.data.persist.certificate.EndorsementCredential; import hirs.data.persist.certificate.IssuedAttestationCertificate; import hirs.data.persist.certificate.PlatformCredential; +import hirs.data.persist.certificate.PlatformCredentialTest; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -17,8 +22,6 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import hirs.data.persist.certificate.CertificateTest; -import hirs.data.persist.certificate.PlatformCredentialTest; import java.io.IOException; import java.math.BigInteger; @@ -34,6 +37,8 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import static hirs.data.persist.certificate.CertificateTest.ISSUED_CLIENT_CERT; + /** * This class tests the storage, retrieval, and deletion of {@link Certificate}s. */ @@ -75,7 +80,7 @@ public class DBCertificateManagerTest extends SpringPersistenceTest { */ @BeforeMethod public void setupTestObjects() throws IOException { - // create indivdual test certificates + // create individual test certificates rootCert = CertificateTest.getTestCertificate(CertificateTest.FAKE_ROOT_CA_FILE); intelIntermediateCert = CertificateTest.getTestCertificate( CertificateTest.FAKE_INTEL_INT_CA_FILE @@ -91,6 +96,9 @@ public class DBCertificateManagerTest extends SpringPersistenceTest { CertificateTest.ANOTHER_SELF_SIGNED_FILE ); + hirsClientCert = CertificateTest.getTestCertificate(IssuedAttestationCertificate.class, + ISSUED_CLIENT_CERT); + stmEkCert = CertificateTest.getTestCertificate(EndorsementCredential.class, CertificateTest.STM_NUC1_EC); @@ -137,7 +145,7 @@ public class DBCertificateManagerTest extends SpringPersistenceTest { IssuedAttestationCertificate issuedCert = (IssuedAttestationCertificate) CertificateTest.getTestCertificate(IssuedAttestationCertificate.class, - CertificateTest.ISSUED_CLIENT_CERT, endorsementCredential, platformCredentials); + ISSUED_CLIENT_CERT, endorsementCredential, platformCredentials); testCertificates.put(IssuedAttestationCertificate.class, issuedCert); } @@ -381,6 +389,34 @@ public class DBCertificateManagerTest extends SpringPersistenceTest { ); } + /** + * Tests that a Certificate can be retrieved by its deviceId. + * @throws IOException if there is a problem creating the certificate + * @throws CertificateException if there is a problem deserializing the original X509Certificate + */ + @Test + public void testGetByDeviceId() throws IOException, CertificateException { + CertificateManager certMan = new DBCertificateManager(sessionFactory); + DeviceManager deviceManager = new DBDeviceManager(sessionFactory); + DeviceGroupManager deviceGroupManager = new DBDeviceGroupManager(sessionFactory); + + Device device = new Device("test_device"); + DeviceGroup dg = new DeviceGroup("Default"); + DeviceGroup savedDg = deviceGroupManager.saveDeviceGroup(dg); + device.setDeviceGroup(savedDg); + Device savedDevice = deviceManager.saveDevice(device); + ((DeviceAssociatedCertificate) hirsClientCert).setDevice(savedDevice); + Certificate savedCert = certMan.save(hirsClientCert); + + Set retrievedCerts = + IssuedAttestationCertificate.select(certMan).byDeviceId(savedDevice.getId()). + getCertificates(); + Assert.assertEquals(retrievedCerts.size(), 1); + for (IssuedAttestationCertificate cert: retrievedCerts) { + Assert.assertEquals(savedCert.getId(), cert.getId()); + } + } + /** * Tests that a single Certificate can be retrieved amongst many stored Certificates according * to its type and subject. diff --git a/HIRS_Utils/src/test/java/hirs/persist/DBDeviceManagerTest.java b/HIRS_Utils/src/test/java/hirs/persist/DBDeviceManagerTest.java index 35910f7e..8f1f3f4c 100644 --- a/HIRS_Utils/src/test/java/hirs/persist/DBDeviceManagerTest.java +++ b/HIRS_Utils/src/test/java/hirs/persist/DBDeviceManagerTest.java @@ -149,6 +149,35 @@ public final class DBDeviceManagerTest extends SpringPersistenceTest { Assert.fail("save did not fail"); } + /** + * Tests that when a Device is deleted, the + * Device is removed from the DB. + * + * @throws Exception if error occurs while creating test device + */ + @Test + public void testDeleteDevice() throws Exception { + LOGGER.debug("testDeleteDevice"); + Assert.assertEquals(DBUtility.getCount(sessionFactory, Device.class), 0); + + final Device device = new Device(deviceName); + final DeviceManager mgr = new DBDeviceManager(sessionFactory); + final DeviceGroup group = createGroup(DeviceGroup.DEFAULT_GROUP); + device.setDeviceGroup(group); + final Device savedDevice = mgr.saveDevice(device); + + Assert.assertEquals(DBUtility.getCount(sessionFactory, Device.class), 1); + Assert.assertTrue(DBUtility.isInDatabase(sessionFactory, Device.class, deviceName)); + + final UUID deviceID = savedDevice.getId(); + Assert.assertNotNull(deviceID); + boolean deleteSuccessful = mgr.deleteDevice(deviceName); + Assert.assertTrue(deleteSuccessful); + Assert.assertFalse( + DBUtility.isInDatabase(sessionFactory, DeviceGroup.class, deviceName) + ); + } + /** * Tests that the DBDeviceManager can update a * Device.