Skip to content

Commit e2577b1

Browse files
committed
add Content-MD5 header for Object Lock compliance
1 parent f71999b commit e2577b1

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

app/server/appsmith-plugins/amazons3Plugin/src/main/java/com/external/plugins/AmazonS3Plugin.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import java.io.IOException;
5959
import java.io.InputStream;
6060
import java.net.URL;
61+
import java.security.MessageDigest;
62+
import java.security.NoSuchAlgorithmException;
6163
import java.text.DateFormat;
6264
import java.text.SimpleDateFormat;
6365
import java.util.ArrayList;
@@ -1167,10 +1169,29 @@ void uploadFileInS3(
11671169
TransferManager transferManager =
11681170
TransferManagerBuilder.standard().withS3Client(connection).build();
11691171
final ObjectMetadata objectMetadata = new ObjectMetadata();
1172+
1173+
// Set content length
1174+
objectMetadata.setContentLength(payload.length);
1175+
11701176
// Only add content type if the user has mentioned it in the body
11711177
if (multipartFormDataDTO.getType() != null) {
11721178
objectMetadata.setContentType(multipartFormDataDTO.getType());
11731179
}
1180+
1181+
// Calculate and set Content-MD5 header for Object Lock compliance
1182+
try {
1183+
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
1184+
byte[] md5Hash = md5Digest.digest(payload);
1185+
String md5Base64 = Base64.getEncoder().encodeToString(md5Hash);
1186+
objectMetadata.setContentMD5(md5Base64);
1187+
log.debug("Set Content-MD5 header for S3 upload: {}", md5Base64);
1188+
} catch (NoSuchAlgorithmException e) {
1189+
log.warn(
1190+
"Failed to calculate MD5 checksum for S3 upload. Object Lock enabled buckets may reject this upload.",
1191+
e);
1192+
// Continue with upload without MD5 header - let AWS handle the error if Object Lock is enabled
1193+
}
1194+
11741195
transferManager
11751196
.upload(bucketName, path, inputStream, objectMetadata)
11761197
.waitForUploadResult();

app/server/appsmith-plugins/amazons3Plugin/src/test/java/com/external/plugins/AmazonS3PluginTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import java.lang.reflect.Method;
4646
import java.net.MalformedURLException;
4747
import java.net.URL;
48+
import java.security.MessageDigest;
49+
import java.security.NoSuchAlgorithmException;
4850
import java.util.ArrayList;
4951
import java.util.Arrays;
5052
import java.util.HashMap;
@@ -1584,4 +1586,47 @@ public void verify_sanitizeGenerateCRUDPageTemplateInfo_addsInfoToReplaceTemplat
15841586
.block();
15851587
assertEquals(userSelectedBucketName, mappedColumnsAndTableName.get("templateBucket"));
15861588
}
1589+
1590+
@Test
1591+
public void testContentMD5CalculationForObjectLock() throws NoSuchAlgorithmException {
1592+
// Test that MD5 calculation works correctly for Object Lock compliance
1593+
String testContent = "Hello, World!";
1594+
byte[] payload = testContent.getBytes();
1595+
1596+
// Calculate MD5 hash using the same logic as in uploadFileInS3
1597+
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
1598+
byte[] md5Hash = md5Digest.digest(payload);
1599+
String md5Base64 = java.util.Base64.getEncoder().encodeToString(md5Hash);
1600+
1601+
// Verify MD5 calculation is correct
1602+
assertNotNull(md5Base64);
1603+
assertTrue(md5Base64.length() > 0);
1604+
1605+
// For "Hello, World!" the MD5 hash should be deterministic
1606+
// Expected MD5 for "Hello, World!" is 65a8e27d8879283831b664bd8b7f0ad4
1607+
String expectedMd5Hex = "65a8e27d8879283831b664bd8b7f0ad4";
1608+
String actualMd5Hex = bytesToHex(md5Hash);
1609+
assertEquals(expectedMd5Hex, actualMd5Hex);
1610+
1611+
// Verify base64 encoding
1612+
String expectedBase64 = java.util.Base64.getEncoder().encodeToString(hexToBytes(expectedMd5Hex));
1613+
assertEquals(expectedBase64, md5Base64);
1614+
}
1615+
1616+
private String bytesToHex(byte[] bytes) {
1617+
StringBuilder result = new StringBuilder();
1618+
for (byte b : bytes) {
1619+
result.append(String.format("%02x", b));
1620+
}
1621+
return result.toString();
1622+
}
1623+
1624+
private byte[] hexToBytes(String hex) {
1625+
int len = hex.length();
1626+
byte[] data = new byte[len / 2];
1627+
for (int i = 0; i < len; i += 2) {
1628+
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
1629+
}
1630+
return data;
1631+
}
15871632
}

0 commit comments

Comments
 (0)