fix: add Content-MD5 header for Object Lock compliance (#41241)

This commit is contained in:
Nilansh Bansal 2025-09-22 14:52:06 +05:30 committed by GitHub
parent f71999b14f
commit 36c34db4ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 66 additions and 0 deletions

View File

@ -58,6 +58,8 @@ import java.io.ByteArrayInputStream;
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;
@ -1167,10 +1169,29 @@ public class AmazonS3Plugin extends BasePlugin {
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();

View File

@ -45,6 +45,8 @@ import java.lang.reflect.InvocationTargetException;
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;
@ -1584,4 +1586,47 @@ public class AmazonS3PluginTest {
.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;
}
}