Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
Expand Down Expand Up @@ -1167,10 +1169,29 @@ void uploadFileInS3(
TransferManager transferManager =
TransferManagerBuilder.standard().withS3Client(connection).build();
final ObjectMetadata objectMetadata = new ObjectMetadata();

// Set content length
objectMetadata.setContentLength(payload.length);

// Only add content type if the user has mentioned it in the body
if (multipartFormDataDTO.getType() != null) {
objectMetadata.setContentType(multipartFormDataDTO.getType());
}

// Calculate and set Content-MD5 header for Object Lock compliance
try {
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
byte[] md5Hash = md5Digest.digest(payload);
String md5Base64 = Base64.getEncoder().encodeToString(md5Hash);
objectMetadata.setContentMD5(md5Base64);
log.debug("Set Content-MD5 header for S3 upload: {}", md5Base64);
} catch (NoSuchAlgorithmException e) {
log.warn(
"Failed to calculate MD5 checksum for S3 upload. Object Lock enabled buckets may reject this upload.",
e);
// Continue with upload without MD5 header - let AWS handle the error if Object Lock is enabled
}

transferManager
.upload(bucketName, path, inputStream, objectMetadata)
.waitForUploadResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
Expand Down Expand Up @@ -1584,4 +1586,47 @@ public void verify_sanitizeGenerateCRUDPageTemplateInfo_addsInfoToReplaceTemplat
.block();
assertEquals(userSelectedBucketName, mappedColumnsAndTableName.get("templateBucket"));
}

@Test
public void testContentMD5CalculationForObjectLock() throws NoSuchAlgorithmException {
// Test that MD5 calculation works correctly for Object Lock compliance
String testContent = "Hello, World!";
byte[] payload = testContent.getBytes();

// Calculate MD5 hash using the same logic as in uploadFileInS3
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
byte[] md5Hash = md5Digest.digest(payload);
String md5Base64 = java.util.Base64.getEncoder().encodeToString(md5Hash);

// Verify MD5 calculation is correct
assertNotNull(md5Base64);
assertTrue(md5Base64.length() > 0);

// For "Hello, World!" the MD5 hash should be deterministic
// Expected MD5 for "Hello, World!" is 65a8e27d8879283831b664bd8b7f0ad4
String expectedMd5Hex = "65a8e27d8879283831b664bd8b7f0ad4";
String actualMd5Hex = bytesToHex(md5Hash);
assertEquals(expectedMd5Hex, actualMd5Hex);

// Verify base64 encoding
String expectedBase64 = java.util.Base64.getEncoder().encodeToString(hexToBytes(expectedMd5Hex));
assertEquals(expectedBase64, md5Base64);
}

private String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}

private byte[] hexToBytes(String hex) {
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
}