Artifact Encryption plug point (#1202)
* added ArtifactEncryption interface, injected it into SM creation UI module, added encryption metadata key generation upon SM creation, used encryptor during file upload Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * add default artifact encryption implementation based on gcm aes algorithm Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * changed ArtifactEncryptor interface to manage encryption secrets by itself Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * cleaned up stale code, fixed sonar Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * fixed software module encryption within transaction Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added artifact encryption secrets store Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * extended ArtifactEncryption interface to allow decryption, secrets store provides removeSecret, added missing javadocs Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * intriduced DbArtifact interface, use EncryptionAwareDbArtifact for artifact decryption during download Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * introduced ArtifactEncryptionService to minimize duplications and unneccessary dependency injections Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * declared ArtifactEncryptionService as a bean Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added persistant encryption flag to software module Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * further adptations for encryption flag persistence Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added ArtifactEncryptionException, fixed encryption check in UI Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added encryption error handling Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * added encrypted flag to DDI/DMF, adapted exception handling Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * adapted rest docs Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * Add test to verify artifact encryption is not given by default Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Add isEncrypted() to toString() of JpaSoftwareModule, fix typos Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Fix sql migration scripts Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * Calculate encrypted artifact size by subtract encryption size overhead Signed-off-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io> * publish upload failed without waiting for interuption during UI file upload Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> * upgraded cron utils to 9.1.6 Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch.io> Co-authored-by: Florian Ruschbaschan <Florian.Ruschbaschan@bosch.io>
This commit is contained in:
@@ -26,7 +26,7 @@ import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
@@ -77,7 +77,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
.isFalse();
|
||||
|
||||
assertThat(artifactManagement.findFirstBySHA1(NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(artifactManagement.loadArtifactBinary(NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(artifactManagement.loadArtifactBinary(NOT_EXIST_ID, module.getId(), module.isEncrypted()))
|
||||
.isNotPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,10 +117,10 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(artifactRepository.findAll()).hasSize(0);
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3"));
|
||||
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
@@ -165,8 +166,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final String artifactData = "test";
|
||||
final int artifactSize = artifactData.length();
|
||||
|
||||
final long smID = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0", null, null)).getId();
|
||||
final long smID = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smIllegalFilenameTest", "1.0"))
|
||||
.getId();
|
||||
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
|
||||
() -> artifactManagement.create(new ArtifactUpload(IOUtils.toInputStream(artifactData, "UTF-8"), smID,
|
||||
illegalFilename, false, artifactSize)));
|
||||
@@ -178,8 +179,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void createArtifactsUntilQuotaIsExceeded() throws IOException {
|
||||
|
||||
// create a software module
|
||||
final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null))
|
||||
.getId();
|
||||
final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")).getId();
|
||||
|
||||
// now create artifacts for this module until the quota is exceeded
|
||||
final long maxArtifacts = quotaManagement.getMaxArtifactsPerSoftwareModule();
|
||||
@@ -217,14 +217,13 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final int artifactSize = Math.toIntExact(quotaManagement.getMaxArtifactSize() / 10);
|
||||
final int numArtifacts = Math.toIntExact(maxBytes / artifactSize);
|
||||
for (int i = 0; i < numArtifacts; ++i) {
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "smd" + i, "1.0", null, null));
|
||||
final JpaSoftwareModule sm = softwareModuleRepository.save(new JpaSoftwareModule(osType, "smd" + i, "1.0"));
|
||||
artifactIds.add(createArtifactForSoftwareModule("file" + i, sm.getId(), artifactSize).getId());
|
||||
}
|
||||
|
||||
// upload one more artifact to trigger the quota exceeded error
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "smd" + numArtifacts, "1.0", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "smd" + numArtifacts, "1.0"));
|
||||
assertThatExceptionOfType(StorageQuotaExceededException.class)
|
||||
.isThrownBy(() -> createArtifactForSoftwareModule("file" + numArtifacts, sm.getId(), artifactSize));
|
||||
|
||||
@@ -240,8 +239,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void createArtifactFailsIfTooLarge() {
|
||||
|
||||
// create a software module
|
||||
final JpaSoftwareModule sm1 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null));
|
||||
final JpaSoftwareModule sm1 = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0"));
|
||||
|
||||
// create an artifact that exceeds the configured quota
|
||||
final long maxSize = quotaManagement.getMaxArtifactSize();
|
||||
@@ -254,7 +252,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void hardDeleteSoftwareModule() throws IOException {
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
|
||||
createArtifactForSoftwareModule("file1", sm.getId(), 5 * 1024);
|
||||
assertThat(artifactRepository.findAll()).hasSize(1);
|
||||
@@ -273,9 +271,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Description("Tests the deletion of a local artifact including metadata.")
|
||||
public void deleteArtifact() throws IOException {
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
|
||||
assertThat(artifactRepository.findAll()).isEmpty();
|
||||
|
||||
@@ -326,9 +324,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void deleteDuplicateArtifacts() throws IOException {
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
@@ -368,9 +366,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException {
|
||||
|
||||
final JpaSoftwareModule sm = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 1", "version 1"));
|
||||
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||
.save(new JpaSoftwareModule(osType, "name 2", "version 2"));
|
||||
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
@@ -382,7 +380,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Artifact artifact2 = createArtifactForSoftwareModule("file2", sm2.getId(), artifactSize,
|
||||
inputStream2);
|
||||
|
||||
assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact2.getSha1Hash()), randomBytes);
|
||||
assertEqualFileContents(
|
||||
artifactManagement.loadArtifactBinary(artifact2.getSha1Hash(), sm2.getId(), sm2.isEncrypted()),
|
||||
randomBytes);
|
||||
|
||||
assertThat(artifactRepository.findAll()).hasSize(2);
|
||||
|
||||
@@ -447,9 +447,11 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final int artifactSize = 5 * 1024;
|
||||
final byte[] randomBytes = randomBytes(artifactSize);
|
||||
try (final InputStream input = new ByteArrayInputStream(randomBytes)) {
|
||||
final Artifact artifact = createArtifactForSoftwareModule("file1",
|
||||
testdataFactory.createSoftwareModuleOs().getId(), artifactSize, input);
|
||||
assertEqualFileContents(artifactManagement.loadArtifactBinary(artifact.getSha1Hash()), randomBytes);
|
||||
final SoftwareModule smOs = testdataFactory.createSoftwareModuleOs();
|
||||
final Artifact artifact = createArtifactForSoftwareModule("file1", smOs.getId(), artifactSize, input);
|
||||
assertEqualFileContents(
|
||||
artifactManagement.loadArtifactBinary(artifact.getSha1Hash(), smOs.getId(), smOs.isEncrypted()),
|
||||
randomBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +461,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() {
|
||||
assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||
.as("Should not have worked with missing permission.")
|
||||
.isThrownBy(() -> artifactManagement.loadArtifactBinary("123"));
|
||||
.isThrownBy(() -> artifactManagement.loadArtifactBinary("123", 1, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -541,7 +543,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(createdArtifact.getSha256Hash()).isEqualTo(artifactHashes.getSha256());
|
||||
}
|
||||
|
||||
final Optional<AbstractDbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
|
||||
final Optional<DbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
|
||||
sm.getId(), sm.isEncrypted());
|
||||
assertThat(dbArtifact).isPresent();
|
||||
}
|
||||
|
||||
@@ -561,7 +564,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Artifact artifact = artifactManagement.create(artifactUpload);
|
||||
assertThat(artifact).isNotNull();
|
||||
}
|
||||
final Optional<AbstractDbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1());
|
||||
final Optional<DbArtifact> dbArtifact = artifactManagement.loadArtifactBinary(artifactHashes.getSha1(),
|
||||
smOs.getId(), smOs.isEncrypted());
|
||||
assertThat(dbArtifact).isPresent();
|
||||
|
||||
try (final InputStream inputStream = new ByteArrayInputStream(testData)) {
|
||||
@@ -622,10 +626,9 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(runAsTenant(tenant, () -> artifactRepository.findAll())).hasSize(count);
|
||||
}
|
||||
|
||||
private void assertEqualFileContents(final Optional<AbstractDbArtifact> artifact, final byte[] randomBytes)
|
||||
private void assertEqualFileContents(final Optional<DbArtifact> artifact, final byte[] randomBytes)
|
||||
throws IOException {
|
||||
try (final InputStream inputStream = artifactManagement.loadArtifactBinary(artifact.get().getHashes().getSha1())
|
||||
.get().getFileInputStream()) {
|
||||
try (final InputStream inputStream = artifact.get().getFileInputStream()) {
|
||||
assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(randomBytes), inputStream),
|
||||
"The stored binary matches the given binary");
|
||||
}
|
||||
|
||||
@@ -520,13 +520,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
Arrays.asList(testType.getId()));
|
||||
|
||||
// found in test
|
||||
final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound");
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a");
|
||||
final SoftwareModule unassigned = testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "a", false);
|
||||
|
||||
// ignored
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
|
||||
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
||||
|
||||
final DistributionSet set = distributionSetManagement
|
||||
@@ -572,13 +572,13 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
||||
Arrays.asList(testType.getId()));
|
||||
|
||||
// found in test
|
||||
testdataFactory.createSoftwareModule("thetype", "unassignedfound");
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound");
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound");
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d");
|
||||
testdataFactory.createSoftwareModule("thetype", "unassignedfound", false);
|
||||
final SoftwareModule one = testdataFactory.createSoftwareModule("thetype", "bfound", false);
|
||||
final SoftwareModule two = testdataFactory.createSoftwareModule("thetype", "cfound", false);
|
||||
final SoftwareModule differentName = testdataFactory.createSoftwareModule("thetype", "d", false);
|
||||
|
||||
// ignored
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted");
|
||||
final SoftwareModule deleted = testdataFactory.createSoftwareModule("thetype", "deleted", false);
|
||||
final SoftwareModule four = testdataFactory.createSoftwareModuleOs("e");
|
||||
|
||||
distributionSetManagement
|
||||
|
||||
Reference in New Issue
Block a user