From bde3548846fbc7b8a31e7e3eb473e8e38288b1f0 Mon Sep 17 00:00:00 2001 From: Alexander Dobler <50330299+dobleralex@users.noreply.github.com> Date: Mon, 29 Jul 2019 14:11:40 +0200 Subject: [PATCH] DDI supports sha256 (#869) * Add SHA256 file hash to ddi GET outputs Signed-off-by: Alexander Dobler * Integrate review findings for SHA256 changes Signed-off-by: Alexander Dobler * Renamed hashes to base16hases in store() parameters Signed-off-by: Alexander Dobler * Added missing javadoc according to sonarqube findings Signed-off-by: Alexander Dobler --- .../ArtifactFilesystemRepository.java | 8 ++- .../repository/ArtifactFilesystemTest.java | 4 +- .../AbstractArtifactRepository.java | 44 ++++++++++------ .../repository/HashNotMatchException.java | 1 + .../repository/model/DbArtifactHash.java | 10 +++- .../AmqpMessageDispatcherServiceTest.java | 4 +- .../hawkbit/repository/model/Artifact.java | 5 ++ .../repository/model/ArtifactUpload.java | 7 +++ .../repository/jpa/JpaArtifactManagement.java | 3 +- .../repository/jpa/model/JpaArtifact.java | 13 +++++ .../DB2/V1_12_14__add_sha256_hash___DB2.sql | 1 + .../H2/V1_12_14__add_sha256_hash___H2.sql | 1 + .../V1_12_14__add_sha256_hash___MYSQL.sql | 1 + ...V1_12_14__add_sha256_hash___SQL_SERVER.sql | 1 + .../jpa/ArtifactManagementTest.java | 2 + .../ddi/json/model/DdiArtifactHash.java | 19 ++++++- .../rest/resource/DataConversionHelper.java | 2 +- .../rest/resource/DdiDeploymentBaseTest.java | 52 ++++++++++--------- .../documentation/DdiApiModelProperties.java | 2 + .../RootControllerDocumentationTest.java | 4 ++ 20 files changed, 131 insertions(+), 53 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_14__add_sha256_hash___DB2.sql create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_14__add_sha256_hash___H2.sql create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_14__add_sha256_hash___MYSQL.sql create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_14__add_sha256_hash___SQL_SERVER.sql diff --git a/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java index f346c1d14..90e9b12b0 100644 --- a/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java +++ b/hawkbit-artifact-repository-filesystem/src/main/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemRepository.java @@ -62,16 +62,14 @@ public class ArtifactFilesystemRepository extends AbstractArtifactRepository { return null; } - return new ArtifactFilesystem(file, sha1, new DbArtifactHash(sha1, null), file.length(), null); + return new ArtifactFilesystem(file, sha1, new DbArtifactHash(sha1, null, null), file.length(), null); } @Override - protected AbstractDbArtifact store(final String tenant, final String sha1Hash16, final String mdMD5Hash16, - final String contentType, final String tempFile) throws IOException { + protected AbstractDbArtifact store(final String tenant, final DbArtifactHash base16Hashes, final String contentType, final String tempFile) throws IOException { final File file = new File(tempFile); - return renameFileToSHA1Naming(tenant, file, new ArtifactFilesystem(file, sha1Hash16, - new DbArtifactHash(sha1Hash16, mdMD5Hash16), file.length(), contentType)); + return renameFileToSHA1Naming(tenant, file, new ArtifactFilesystem(file, base16Hashes.getSha1(), base16Hashes, file.length(), contentType)); } private ArtifactFilesystem renameFileToSHA1Naming(final String tenant, final File file, diff --git a/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java b/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java index ab66519b3..9669a88aa 100644 --- a/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java +++ b/hawkbit-artifact-repository-filesystem/src/test/java/org/eclipse/hawkbit/artifact/repository/ArtifactFilesystemTest.java @@ -32,7 +32,7 @@ public class ArtifactFilesystemTest { public void getInputStreamOfNonExistingFileThrowsException() { final File file = new File("fileWhichTotalDoesNotExists"); final ArtifactFilesystem underTest = new ArtifactFilesystem(file, "fileWhichTotalDoesNotExists", - new DbArtifactHash("1", "2"), 0L, null); + new DbArtifactHash("1", "2", "3"), 0L, null); try { underTest.getFileInputStream(); Assertions.fail("Expected a FileNotFoundException because file does not exists"); @@ -48,7 +48,7 @@ public class ArtifactFilesystemTest { createTempFile.deleteOnExit(); final ArtifactFilesystem underTest = new ArtifactFilesystem(createTempFile, - ArtifactFilesystemTest.class.getSimpleName(), new DbArtifactHash("1", "2"), 0L, null); + ArtifactFilesystemTest.class.getSimpleName(), new DbArtifactHash("1", "2", "3"), 0L, null); final byte[] buffer = new byte[1024]; IOUtils.read(underTest.getFileInputStream(), buffer); } diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/AbstractArtifactRepository.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/AbstractArtifactRepository.java index de9ddcdec..ccbab4b70 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/AbstractArtifactRepository.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/AbstractArtifactRepository.java @@ -42,27 +42,31 @@ public abstract class AbstractArtifactRepository implements ArtifactRepository { // is not used security related @SuppressWarnings("squid:S2070") public AbstractDbArtifact store(final String tenant, final InputStream content, final String filename, - final String contentType, final DbArtifactHash hash) { + final String contentType, final DbArtifactHash providedHashes) { final MessageDigest mdSHA1; final MessageDigest mdMD5; + final MessageDigest mdSHA256; try { mdSHA1 = MessageDigest.getInstance("SHA1"); mdMD5 = MessageDigest.getInstance("MD5"); + mdSHA256 = MessageDigest.getInstance("SHA-256"); } catch (final NoSuchAlgorithmException e) { throw new ArtifactStoreException(e.getMessage(), e); } String tempFile = null; - try (final DigestInputStream inputstream = wrapInDigestInputStream(content, mdSHA1, mdMD5)) { + try (final DigestInputStream inputstream = wrapInDigestInputStream(content, mdSHA1, mdMD5, mdSHA256)) { tempFile = storeTempFile(inputstream); final String sha1Hash16 = BaseEncoding.base16().lowerCase().encode(mdSHA1.digest()); final String md5Hash16 = BaseEncoding.base16().lowerCase().encode(mdMD5.digest()); + final String sha256Hash16 = BaseEncoding.base16().lowerCase().encode(mdSHA256.digest()); - checkHashes(sha1Hash16, md5Hash16, hash); + checkHashes(sha1Hash16, md5Hash16, sha256Hash16, providedHashes); - return store(sanitizeTenant(tenant), sha1Hash16, md5Hash16, contentType, tempFile); + return store(sanitizeTenant(tenant), new DbArtifactHash(sha1Hash16, md5Hash16, sha256Hash16), contentType, + tempFile); } catch (final IOException e) { throw new ArtifactStoreException(e.getMessage(), e); } finally { @@ -100,27 +104,35 @@ public abstract class AbstractArtifactRepository implements ArtifactRepository { } } - private static void checkHashes(final String sha1Hash16, final String md5Hash16, final DbArtifactHash hash) { - if (hash == null) { + private static void checkHashes(final String sha1Hash16, final String md5Hash16, final String sha256Hash16, + final DbArtifactHash providedHashes) { + if (providedHashes == null) { return; } - if (hash.getSha1() != null && !sha1Hash16.equals(hash.getSha1())) { - throw new HashNotMatchException("The given sha1 hash " + hash.getSha1() - + " does not match with the calcualted sha1 hash " + sha1Hash16, HashNotMatchException.SHA1); + if (areHashesNotMatching(providedHashes.getSha1(), sha1Hash16)) { + throw new HashNotMatchException("The given sha1 hash " + providedHashes.getSha1() + + " does not match the calculated sha1 hash " + sha1Hash16, HashNotMatchException.SHA1); } - if (hash.getMd5() != null && !md5Hash16.equals(hash.getMd5())) { - throw new HashNotMatchException( - "The given md5 hash " + hash.getMd5() + " does not match with the calcualted md5 hash " + md5Hash16, - HashNotMatchException.MD5); + if (areHashesNotMatching(providedHashes.getMd5(), md5Hash16)) { + throw new HashNotMatchException("The given md5 hash " + providedHashes.getMd5() + + " does not match the calculated md5 hash " + md5Hash16, HashNotMatchException.MD5); + } + if (areHashesNotMatching(providedHashes.getSha256(), sha256Hash16)) { + throw new HashNotMatchException("The given sha256 hash " + providedHashes.getSha256() + + " does not match the calculated sha256 hash " + sha256Hash16, HashNotMatchException.SHA256); } } - protected abstract AbstractDbArtifact store(final String tenant, final String sha1Hash16, final String mdMD5Hash16, + private static boolean areHashesNotMatching(String providedHashValue, String hashValue) { + return providedHashValue != null && !hashValue.equals(providedHashValue); + } + + protected abstract AbstractDbArtifact store(final String tenant, final DbArtifactHash base16Hashes, final String contentType, final String tempFile) throws IOException; private static DigestInputStream wrapInDigestInputStream(final InputStream input, final MessageDigest mdSHA1, - final MessageDigest mdMD5) { - return new DigestInputStream(new DigestInputStream(input, mdMD5), mdSHA1); + final MessageDigest mdMD5, final MessageDigest mdSHA256) { + return new DigestInputStream(new DigestInputStream(new DigestInputStream(input, mdSHA256), mdMD5), mdSHA1); } protected static String sanitizeTenant(final String tenant) { diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/HashNotMatchException.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/HashNotMatchException.java index 0c15d817e..17073bbe9 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/HashNotMatchException.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/HashNotMatchException.java @@ -21,6 +21,7 @@ public class HashNotMatchException extends RuntimeException { public static final String SHA1 = "SHA-1"; public static final String MD5 = "MD5"; + public static final String SHA256 = "SHA-256"; private final String hashFunction; diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/DbArtifactHash.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/DbArtifactHash.java index 3a52f82aa..2c6112aed 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/DbArtifactHash.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/DbArtifactHash.java @@ -18,6 +18,8 @@ public class DbArtifactHash { private final String md5; + private final String sha256; + /** * Constructor. * @@ -25,10 +27,13 @@ public class DbArtifactHash { * the sha1 hash * @param md5 * the md5 hash + * @param sha256 + * the sha256 hash */ - public DbArtifactHash(final String sha1, final String md5) { + public DbArtifactHash(final String sha1, final String md5, final String sha256) { this.sha1 = sha1; this.md5 = md5; + this.sha256 = sha256; } public String getSha1() { @@ -39,4 +44,7 @@ public class DbArtifactHash { return md5; } + public String getSha256() { + return sha256; + } } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java index 72381c4b1..5f1332c17 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java @@ -167,14 +167,14 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { } @Test - @Description("Verifies that download and install event with software moduls and artifacts works") + @Description("Verifies that download and install event with software modules and artifacts works") public void testSendDownloadRequest() { DistributionSet dsA = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); SoftwareModule module = dsA.getModules().iterator().next(); final List receivedList = new ArrayList<>(); for (final Artifact artifact : testdataFactory.createArtifacts(module.getId())) { receivedList.add(new ArtifactFilesystem(new File("./test"), artifact.getSha1Hash(), - new DbArtifactHash(artifact.getSha1Hash(), null), artifact.getSize(), null)); + new DbArtifactHash(artifact.getSha1Hash(), null, null), artifact.getSize(), null)); } module = softwareModuleManagement.get(module.getId()).get(); dsA = distributionSetManagement.get(dsA.getId()).get(); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java index 10aa519e5..e3960f3ec 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Artifact.java @@ -38,6 +38,11 @@ public interface Artifact extends TenantAwareBaseEntity { */ String getSha1Hash(); + /** + * @return SHA-256 hash of the artifact. + */ + String getSha256Hash(); + /** * @return size of the artifact in bytes. */ diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ArtifactUpload.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ArtifactUpload.java index 401cdce03..fe6ee39e5 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ArtifactUpload.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/ArtifactUpload.java @@ -35,6 +35,8 @@ public class ArtifactUpload { private final String providedSha1Sum; + private final String providedSha256Sum; + private final boolean overrideExisting; private final String contentType; @@ -90,6 +92,7 @@ public class ArtifactUpload { this.filename = filename; this.providedMd5Sum = providedMd5Sum; this.providedSha1Sum = providedSha1Sum; + this.providedSha256Sum = null; this.overrideExisting = overrideExisting; this.contentType = contentType; this.filesize = filesize; @@ -115,6 +118,10 @@ public class ArtifactUpload { return providedSha1Sum; } + public String getProvidedSha256Sum() { + return providedSha256Sum; + } + public boolean overrideExisting() { return overrideExisting; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java index a8bbea667..a66ac0f58 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaArtifactManagement.java @@ -128,7 +128,7 @@ public class JpaArtifactManagement implements ArtifactManagement { try { return artifactRepository.store(tenantAware.getCurrentTenant(), artifactUpload.getInputStream(), artifactUpload.getFilename(), artifactUpload.getContentType(), - new DbArtifactHash(artifactUpload.getProvidedSha1Sum(), artifactUpload.getProvidedMd5Sum())); + new DbArtifactHash(artifactUpload.getProvidedSha1Sum(), artifactUpload.getProvidedMd5Sum(), artifactUpload.getProvidedSha256Sum())); } catch (final ArtifactStoreException e) { throw new ArtifactUploadFailedException(e); } catch (final HashNotMatchException e) { @@ -256,6 +256,7 @@ public class JpaArtifactManagement implements ArtifactManagement { artifact = new JpaArtifact(result.getHashes().getSha1(), providedFilename, softwareModule); } artifact.setMd5Hash(result.getHashes().getMd5()); + artifact.setSha256Hash(result.getHashes().getSha256()); artifact.setSha1Hash(result.getHashes().getSha1()); artifact.setSize(result.getSize()); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java index 317e8d3ef..0b4d00578 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaArtifact.java @@ -57,6 +57,9 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art @Column(name = "md5_hash", length = 32, updatable = false, nullable = true) private String md5Hash; + @Column(name = "sha256_hash", length = 64, updatable = false, nullable = true) + private String sha256Hash; + @Column(name = "file_size", updatable = false) private long size; @@ -94,6 +97,11 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art return sha1Hash; } + @Override + public String getSha256Hash() { + return sha256Hash; + } + public void setMd5Hash(final String md5Hash) { this.md5Hash = md5Hash; } @@ -102,6 +110,11 @@ public class JpaArtifact extends AbstractJpaTenantAwareBaseEntity implements Art this.sha1Hash = sha1Hash; } + public void setSha256Hash(final String sha256Hash) { + this.sha256Hash = sha256Hash; + } + + @Override public long getSize() { return size; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_14__add_sha256_hash___DB2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_14__add_sha256_hash___DB2.sql new file mode 100644 index 000000000..ca7e53fce --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_14__add_sha256_hash___DB2.sql @@ -0,0 +1 @@ +ALTER TABLE sp_artifact ADD COLUMN sha256_hash CHAR(64); \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_14__add_sha256_hash___H2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_14__add_sha256_hash___H2.sql new file mode 100644 index 000000000..ca7e53fce --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_14__add_sha256_hash___H2.sql @@ -0,0 +1 @@ +ALTER TABLE sp_artifact ADD COLUMN sha256_hash CHAR(64); \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_14__add_sha256_hash___MYSQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_14__add_sha256_hash___MYSQL.sql new file mode 100644 index 000000000..ca7e53fce --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_14__add_sha256_hash___MYSQL.sql @@ -0,0 +1 @@ +ALTER TABLE sp_artifact ADD COLUMN sha256_hash CHAR(64); \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_14__add_sha256_hash___SQL_SERVER.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_14__add_sha256_hash___SQL_SERVER.sql new file mode 100644 index 000000000..c72b8b124 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_14__add_sha256_hash___SQL_SERVER.sql @@ -0,0 +1 @@ +ALTER TABLE sp_artifact ADD sha256_hash CHAR(64); \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java index da27566cb..9a9e544a0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java @@ -139,6 +139,8 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest { .isEqualTo(HashGeneratorUtils.generateSHA1(randomBytes)); assertThat(artifactManagement.getByFilename("file1").get().getMd5Hash()) .isEqualTo(HashGeneratorUtils.generateMD5(randomBytes)); + assertThat(artifactManagement.getByFilename("file1").get().getSha256Hash()) + .isEqualTo(HashGeneratorUtils.generateSHA256(randomBytes)); assertThat(artifactRepository.findAll()).hasSize(4); assertThat(softwareModuleRepository.findAll()).hasSize(3); diff --git a/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiArtifactHash.java b/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiArtifactHash.java index 898ee244a..c5b00e50a 100644 --- a/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiArtifactHash.java +++ b/hawkbit-rest/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiArtifactHash.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.ddi.json.model; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; /** @@ -23,6 +25,10 @@ public class DdiArtifactHash { @JsonProperty private String md5; + @JsonProperty + @JsonInclude(Include.NON_NULL) + private String sha256; + /** * Default constructor. */ @@ -34,11 +40,16 @@ public class DdiArtifactHash { * Public constructor. * * @param sha1 + * sha1 hash of the artifact * @param md5 + * md5 hash of the artifact + * @param sha256 + * sha256 hash of the artifact */ - public DdiArtifactHash(final String sha1, final String md5) { + public DdiArtifactHash(final String sha1, final String md5, final String sha256) { this.sha1 = sha1; this.md5 = md5; + this.sha256 = sha256; } /** @@ -55,4 +66,10 @@ public class DdiArtifactHash { return md5; } + /** + * @return the sha256 + */ + public String getSha256() { + return sha256; + } } diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java index 110eb0b4c..1e915424d 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/main/java/org/eclipse/hawkbit/ddi/rest/resource/DataConversionHelper.java @@ -92,7 +92,7 @@ public final class DataConversionHelper { private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler, final Artifact artifact, final SystemManagement systemManagement, final HttpRequest request) { final DdiArtifact file = new DdiArtifact(); - file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash())); + file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash(), artifact.getSha256Hash())); file.setFilename(artifact.getFilename()); file.setSize(artifact.getSize()); diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index c276d759d..ae369c46d 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -153,8 +153,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { getOsModule(ds), "test1.signature", ARTIFACT_SIZE); final Target savedTarget = createTargetAndAssertNoActiveActions(); - List targetsAssignedToDs = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.FORCED, - RepositoryModelConstants.NO_FORCE_TIME, Collections.singletonList(savedTarget.getControllerId())) + final List targetsAssignedToDs = deploymentManagement + .assignDistributionSet(ds.getId(), ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME, + Collections.singletonList(savedTarget.getControllerId())) .getAssignedEntity(); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); @@ -172,7 +173,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2); // Run test - long current = System.currentTimeMillis(); + final long current = System.currentTimeMillis(); performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID) .andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00"))) @@ -255,7 +256,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final Target savedTarget = createTargetAndAssertNoActiveActions(); - List saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.SOFT, + final List saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.SOFT, RepositoryModelConstants.NO_FORCE_TIME, Collections.singletonList(savedTarget.getControllerId())) .getAssignedEntity(); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); @@ -315,7 +316,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final Target savedTarget = createTargetAndAssertNoActiveActions(); - List saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.TIMEFORCED, + final List saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.TIMEFORCED, System.currentTimeMillis(), Collections.singletonList(savedTarget.getControllerId())) .getAssignedEntity(); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); @@ -334,7 +335,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { // Run test - long current = System.currentTimeMillis(); + final long current = System.currentTimeMillis(); performGet(CONTROLLER_BY_ID, MediaTypes.HAL_JSON, status().isOk(), tenantAware.getCurrentTenant(), DEFAULT_CONTROLLER_ID) .andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00"))) @@ -383,7 +384,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final Target savedTarget = createTargetAndAssertNoActiveActions(); - List saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.DOWNLOAD_ONLY, + final List saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.DOWNLOAD_ONLY, RepositoryModelConstants.NO_FORCE_TIME, Collections.singletonList(savedTarget.getControllerId())) .getAssignedEntity(); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); @@ -465,6 +466,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { contains(artifact.getMd5Hash()))) .andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.sha1", contains(artifact.getSha1Hash()))) + .andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[0].hashes.sha256", + contains(artifact.getSha256Hash()))) .andExpect(jsonPath( "$.deployment.chunks[?(@.part=='os')].artifacts[0]._links.download-http.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" @@ -480,6 +483,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { contains(artifactSignature.getMd5Hash()))) .andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.sha1", contains(artifactSignature.getSha1Hash()))) + .andExpect(jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1].hashes.sha256", + contains(artifactSignature.getSha256Hash()))) .andExpect( jsonPath("$.deployment.chunks[?(@.part=='os')].artifacts[1]._links.download-http.href", contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/" @@ -710,7 +715,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { postFeedback(MediaType.APPLICATION_JSON, DEFAULT_CONTROLLER_ID, 1234L, JsonBuilder.deploymentActionInProgressFeedback("1234"), status().isNotFound()); - Target savedTarget = testdataFactory.createTarget(DEFAULT_CONTROLLER_ID); + final Target savedTarget = testdataFactory.createTarget(DEFAULT_CONTROLLER_ID); // Action does not exists postFeedback(MediaType.APPLICATION_JSON, "4713", 1234L, JsonBuilder.deploymentActionInProgressFeedback("1234"), @@ -774,9 +779,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final String missingResultInFeedback = JsonBuilder.missingResultInFeedback(action.getId().toString(), "closed", "test"); postFeedback(MediaType.APPLICATION_JSON, "1080", action.getId(), missingResultInFeedback, - status().isBadRequest()) - .andExpect(jsonPath("$.*", hasSize(3))) - .andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName()))); + status().isBadRequest()).andExpect(jsonPath("$.*", hasSize(3))).andExpect( + jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName()))); } @Test @@ -797,13 +801,12 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final String missingFinishedResultInFeedback = JsonBuilder .missingFinishedResultInFeedback(action.getId().toString(), "closed", "test"); postFeedback(MediaType.APPLICATION_JSON, "1080", action.getId(), missingFinishedResultInFeedback, - status().isBadRequest()) - .andExpect(jsonPath("$.*", hasSize(3))) - .andExpect(jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName()))); + status().isBadRequest()).andExpect(jsonPath("$.*", hasSize(3))).andExpect( + jsonPath("$.exceptionClass", equalTo(MessageNotReadableException.class.getCanonicalName()))); } private void assertActionStatusCount(final int actionStatusCount, final int minActionStatusCountInPage) { - Target target = targetManagement.getByControllerID(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID).get(); + final Target target = targetManagement.getByControllerID(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID).get(); assertThat(target.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING); assertTargetCountByStatus(1, 0, 0); @@ -834,16 +837,17 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { .andExpect(content().contentTypeCompatibleWith(mediaType)); } - private ResultActions postFeedback(final MediaType mediaType, final String controllerId, final Long id, final String content, - final ResultMatcher statusMatcher) throws Exception { + private ResultActions postFeedback(final MediaType mediaType, final String controllerId, final Long id, + final String content, final ResultMatcher statusMatcher) throws Exception { return postFeedback(mediaType, controllerId, id, content.getBytes(), statusMatcher); } - private ResultActions postFeedback(final MediaType mediaType, final String controllerId, final Long id, final byte[] content, - final ResultMatcher statusMatcher) throws Exception { - return mvc.perform(post(DEPLOYMENT_BASE + id + "/feedback", tenantAware.getCurrentTenant(), controllerId) - .content(content).contentType(mediaType).accept(mediaType)).andDo(MockMvcResultPrinter.print()) - .andExpect(statusMatcher); + private ResultActions postFeedback(final MediaType mediaType, final String controllerId, final Long id, + final byte[] content, final ResultMatcher statusMatcher) throws Exception { + return mvc + .perform(post(DEPLOYMENT_BASE + id + "/feedback", tenantAware.getCurrentTenant(), controllerId) + .content(content).contentType(mediaType).accept(mediaType)) + .andDo(MockMvcResultPrinter.print()).andExpect(statusMatcher); } private Target createTargetAndAssertNoActiveActions() { @@ -855,7 +859,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { } private void assertStatusMessagesCount(final int actionStatusMessagesCount) { - Iterable actionStatusMessages; + final Iterable actionStatusMessages; actionStatusMessages = deploymentManagement.findActionStatusAll(PageRequest.of(0, 100, Direction.DESC, "id")) .getContent(); assertThat(actionStatusMessages).hasSize(actionStatusMessagesCount); @@ -894,7 +898,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { } private void assertStatusAndActiveActionsCount(final TargetUpdateStatus status, final int activeActions) { - Target target = targetManagement.getByControllerID(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID).get(); + final Target target = targetManagement.getByControllerID(DdiDeploymentBaseTest.DEFAULT_CONTROLLER_ID).get(); assertThat(target.getUpdateStatus()).isEqualTo(status); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())) .hasSize(activeActions); diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java index 44708fc7e..b6c22b782 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java @@ -112,6 +112,8 @@ final class DdiApiModelProperties { static final String ARTIFACT_HASHES_SHA1 = "SHA1 hash of the artifact in Base 16 format"; static final String ARTIFACT_HASHES_MD5 = "MD5 hash of the artifact"; + static final String ARTIFACT_HASHES_SHA256 = "SHA-256 hash of the artifact in Base 16 format"; + static final String ARTIFACT_SIZE = "size of the artifact"; static final String ACTION_HISTORY = "Optional GET parameter to retrieve a given number of messages which are previously provided by the device. " diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java index c0907ff17..6ff82b195 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java @@ -321,6 +321,8 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio .description(DdiApiModelProperties.ARTIFACT_HASHES_SHA1), fieldWithPath("deployment.chunks[].artifacts[].hashes.md5") .description(DdiApiModelProperties.ARTIFACT_HASHES_MD5), + fieldWithPath("deployment.chunks[].artifacts[].hashes.sha256") + .description(DdiApiModelProperties.ARTIFACT_HASHES_SHA256), fieldWithPath("deployment.chunks[].artifacts[].size") .description(DdiApiModelProperties.ARTIFACT_SIZE), fieldWithPath("deployment.chunks[].artifacts[]._links.download") @@ -451,6 +453,8 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio fieldWithPath("[]hashes").description(DdiApiModelProperties.ARTIFACTS), fieldWithPath("[]hashes.sha1").description(DdiApiModelProperties.ARTIFACT_HASHES_SHA1), fieldWithPath("[]hashes.md5").description(DdiApiModelProperties.ARTIFACT_HASHES_MD5), + fieldWithPath("[]hashes.sha256") + .description(DdiApiModelProperties.ARTIFACT_HASHES_SHA256), fieldWithPath("[]size").description(DdiApiModelProperties.ARTIFACT_SIZE), fieldWithPath("[]_links.download") .description(DdiApiModelProperties.ARTIFACT_HTTPS_DOWNLOAD_LINK_BY_CONTROLLER),