Feature/enforce hash validation for uploads (#1158)

* moved artifact duplication check after hash check

Signed-off-by: Robert Sing <robert.sing@bosch-si.com>

* added unit tests

Signed-off-by: Robert Sing <robert.sing@bosch-si.com>

* fixed copyright header

Signed-off-by: Robert Sing <robert.sing@bosch-si.com>

* fixed review finding

Signed-off-by: Robert Sing <robert.sing@bosch-si.com>
This commit is contained in:
Robert Sing
2021-08-05 14:53:36 +02:00
committed by GitHub
parent 101401a6a8
commit 2574581b2c
4 changed files with 146 additions and 25 deletions

View File

@@ -55,9 +55,9 @@ public abstract class AbstractArtifactRepository implements ArtifactRepository {
}
String tempFile = null;
try (final DigestInputStream inputstream = wrapInDigestInputStream(content, mdSHA1, mdMD5, mdSHA256)) {
try (final DigestInputStream inputStream = wrapInDigestInputStream(content, mdSHA1, mdMD5, mdSHA256)) {
tempFile = storeTempFile(inputstream);
tempFile = storeTempFile(inputStream);
final String sha1Hash16 = BaseEncoding.base16().lowerCase().encode(mdSHA1.digest());
final String md5Hash16 = BaseEncoding.base16().lowerCase().encode(mdMD5.digest());
@@ -65,6 +65,11 @@ public abstract class AbstractArtifactRepository implements ArtifactRepository {
checkHashes(sha1Hash16, md5Hash16, sha256Hash16, providedHashes);
// Check if file with same sha1 hash exists and if so return it
if (existsByTenantAndSha1(tenant, sha1Hash16)) {
return addMissingHashes(getArtifactBySha1(tenant, sha1Hash16), sha1Hash16, md5Hash16, sha256Hash16);
}
return store(sanitizeTenant(tenant), new DbArtifactHash(sha1Hash16, md5Hash16, sha256Hash16), contentType,
tempFile);
} catch (final IOException e) {
@@ -76,6 +81,21 @@ public abstract class AbstractArtifactRepository implements ArtifactRepository {
}
}
private AbstractDbArtifact addMissingHashes(final AbstractDbArtifact existing, final String calculatedSha1,
final String calculatedMd5, final String calculatedSha256) {
final String sha1 = checkEmpty(existing.getHashes().getSha1(), calculatedSha1);
final String md5 = checkEmpty(existing.getHashes().getMd5(), calculatedMd5);
final String sha256 = checkEmpty(existing.getHashes().getSha256(), calculatedSha256);
existing.setHashes(new DbArtifactHash(sha1, md5, sha256));
return existing;
}
private String checkEmpty(final String value, final String fallback) {
return StringUtils.isEmpty(value) ? fallback : value;
}
protected void deleteTempFile(final String tempFile) {
final File file = new File(tempFile);
@@ -118,12 +138,14 @@ public abstract class AbstractArtifactRepository implements ArtifactRepository {
+ " 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);
throw new HashNotMatchException(
"The given sha256 hash " + providedHashes.getSha256()
+ " does not match the calculated sha256 hash " + sha256Hash16,
HashNotMatchException.SHA256);
}
}
private static boolean areHashesNotMatching(String providedHashValue, String hashValue) {
private static boolean areHashesNotMatching(final String providedHashValue, final String hashValue) {
return providedHashValue != null && !hashValue.equals(providedHashValue);
}

View File

@@ -19,10 +19,11 @@ import org.springframework.util.Assert;
public abstract class AbstractDbArtifact {
private final String artifactId;
private final DbArtifactHash hashes;
private final long size;
private final String contentType;
private DbArtifactHash hashes;
protected AbstractDbArtifact(final String artifactId, final DbArtifactHash hashes, final long size,
final String contentType) {
Assert.notNull(artifactId, "Artifact ID cannot be null");
@@ -47,6 +48,13 @@ public abstract class AbstractDbArtifact {
return hashes;
}
/**
* Set hashes of the artifact
*/
public void setHashes(final DbArtifactHash hashes) {
this.hashes = hashes;
}
/**
* @return site of the artifact in bytes
*/
@@ -62,8 +70,8 @@ public abstract class AbstractDbArtifact {
}
/**
* Creates an {@link InputStream} on this artifact. Caller has to take care
* of closing the stream. Repeatable calls open a new {@link InputStream}.
* Creates an {@link InputStream} on this artifact. Caller has to take care of
* closing the stream. Repeatable calls open a new {@link InputStream}.
*
* @return {@link InputStream} to read from artifact.
*/