Skip to content

Commit de70eaf

Browse files
Add server side encryption with kms key for blob store (#74)
1 parent 914e3c7 commit de70eaf

File tree

15 files changed

+451
-19
lines changed

15 files changed

+451
-19
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ target/
33
.classpath
44
.project
55
.idea/
6+
.claude/
67
*.iml
78
*.eml
89
*.ipr
@@ -17,4 +18,4 @@ nb-configuration.xml
1718
nbactions.xml
1819
dependency-reduced-pom.xml
1920
.vscode
20-
.flattened-pom.xml
21+
.flattened-pom.xml

blob/blob-ali/src/main/java/com/salesforce/multicloudj/blob/ali/AliTransformer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ protected ObjectMetadata generateObjectMetadata(UploadRequest uploadRequest) {
7474
ObjectMetadata metadata = new ObjectMetadata();
7575
metadata.setUserMetadata(uploadRequest.getMetadata());
7676
metadata.setObjectTagging(uploadRequest.getTags());
77+
78+
if (uploadRequest.getKmsKeyId() != null && !uploadRequest.getKmsKeyId().isEmpty()) {
79+
metadata.setServerSideEncryption(ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION);
80+
metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION_KEY_ID, uploadRequest.getKmsKeyId());
81+
}
82+
7783
return metadata;
7884
}
7985

blob/blob-ali/src/test/java/com/salesforce/multicloudj/blob/ali/AliTransformerTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
3939
import static org.junit.jupiter.api.Assertions.assertEquals;
4040
import static org.junit.jupiter.api.Assertions.assertNotNull;
41+
import static org.junit.jupiter.api.Assertions.assertNull;
4142
import static org.junit.jupiter.api.Assertions.assertTrue;
4243
import static org.mockito.Mockito.doReturn;
4344
import static org.mockito.Mockito.mock;
@@ -82,6 +83,46 @@ void testToPutObjectRequest() {
8283
assertEquals(file, actual.getFile());
8384
}
8485

86+
@Test
87+
void testToPutObjectRequestWithKmsKey() {
88+
var key = "some-key";
89+
var metadata = Map.of("some-key", "some-value");
90+
var kmsKeyId = "alias/my-kms-key";
91+
92+
var request = UploadRequest
93+
.builder()
94+
.withKey(key)
95+
.withMetadata(metadata)
96+
.withKmsKeyId(kmsKeyId)
97+
.build();
98+
InputStream inputStream = mock(InputStream.class);
99+
100+
var actual = transformer.toPutObjectRequest(request, inputStream);
101+
assertEquals(BUCKET, actual.getBucketName());
102+
assertEquals(key, actual.getKey());
103+
assertEquals(metadata, actual.getMetadata().getUserMetadata());
104+
assertEquals(ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION, actual.getMetadata().getServerSideEncryption());
105+
}
106+
107+
@Test
108+
void testToPutObjectRequestWithoutKmsKey() {
109+
var key = "some-key";
110+
var metadata = Map.of("some-key", "some-value");
111+
112+
var request = UploadRequest
113+
.builder()
114+
.withKey(key)
115+
.withMetadata(metadata)
116+
.build();
117+
InputStream inputStream = mock(InputStream.class);
118+
119+
var actual = transformer.toPutObjectRequest(request, inputStream);
120+
assertEquals(BUCKET, actual.getBucketName());
121+
assertEquals(key, actual.getKey());
122+
assertEquals(metadata, actual.getMetadata().getUserMetadata());
123+
assertNull(actual.getMetadata().getServerSideEncryption());
124+
}
125+
85126
@Test
86127
void testToUploadResponse() {
87128
UploadRequest request = UploadRequest

blob/blob-aws/src/main/java/com/salesforce/multicloudj/blob/aws/AwsTransformer.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,19 @@ public PutObjectRequest toRequest(UploadRequest request) {
138138
List<Tag> tags = request.getTags().entrySet().stream()
139139
.map(entry -> Tag.builder().key(entry.getKey()).value(entry.getValue()).build())
140140
.collect(Collectors.toList());
141-
return PutObjectRequest
141+
PutObjectRequest.Builder builder = PutObjectRequest
142142
.builder()
143143
.bucket(getBucket())
144144
.key(request.getKey())
145145
.metadata(request.getMetadata())
146-
.tagging(Tagging.builder().tagSet(tags).build())
147-
.build();
146+
.tagging(Tagging.builder().tagSet(tags).build());
147+
148+
if (request.getKmsKeyId() != null && !request.getKmsKeyId().isEmpty()) {
149+
builder.serverSideEncryption("aws:kms")
150+
.ssekmsKeyId(request.getKmsKeyId());
151+
}
152+
153+
return builder.build();
148154
}
149155

150156
public GetObjectRequest toRequest(DownloadRequest request) {

blob/blob-aws/src/test/java/com/salesforce/multicloudj/blob/aws/AwsTransformerTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,50 @@ void testUpload() {
103103
assertEquals(expected, transformer.toRequest(request));
104104
}
105105

106+
@Test
107+
void testUploadWithKmsKey() {
108+
var key = "some-key";
109+
var metadata = Map.of("some-key", "some-value");
110+
var tags = Map.of("tag-key", "tag-value");
111+
var kmsKeyId = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012";
112+
113+
var request = UploadRequest
114+
.builder()
115+
.withKey(key)
116+
.withMetadata(metadata)
117+
.withTags(tags)
118+
.withKmsKeyId(kmsKeyId)
119+
.build();
120+
121+
var actual = transformer.toRequest(request);
122+
123+
assertEquals(BUCKET, actual.bucket());
124+
assertEquals(key, actual.key());
125+
assertEquals(metadata, actual.metadata());
126+
assertEquals("aws:kms", actual.serverSideEncryptionAsString());
127+
assertEquals(kmsKeyId, actual.ssekmsKeyId());
128+
}
129+
130+
@Test
131+
void testUploadWithoutKmsKey() {
132+
var key = "some-key";
133+
var metadata = Map.of("some-key", "some-value");
134+
135+
var request = UploadRequest
136+
.builder()
137+
.withKey(key)
138+
.withMetadata(metadata)
139+
.build();
140+
141+
var actual = transformer.toRequest(request);
142+
143+
assertEquals(BUCKET, actual.bucket());
144+
assertEquals(key, actual.key());
145+
assertEquals(metadata, actual.metadata());
146+
assertNull(actual.serverSideEncryptionAsString());
147+
assertNull(actual.ssekmsKeyId());
148+
}
149+
106150
@Test
107151
void testListBlobsBatch() {
108152
var prefixes = Arrays.asList("some/prefix", "some/other/prefix");

blob/blob-client/src/main/java/com/salesforce/multicloudj/blob/driver/DownloadRequest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ public class DownloadRequest {
1212
private final String versionId;
1313
private final Long start;
1414
private final Long end;
15+
private final String kmsKeyId;
1516

1617
private DownloadRequest(Builder builder) {
1718
this.key = builder.key;
1819
this.versionId = builder.versionId;
1920
this.start = builder.start;
2021
this.end = builder.end;
22+
this.kmsKeyId = builder.kmsKeyId;
2123
}
2224

2325
public static Builder builder() {
@@ -29,6 +31,7 @@ public static class Builder {
2931
private String versionId;
3032
private Long start;
3133
private Long end;
34+
private String kmsKeyId;
3235

3336
/**
3437
* Specifies the key of the Blob to download.
@@ -78,6 +81,15 @@ public Builder withRange(Long start, Long end) {
7881
return this;
7982
}
8083

84+
/**
85+
* (Optional) Specifies the KMS key ID or ARN to use for decrypting the blob.
86+
* This is only needed if the blob was encrypted with a customer-managed KMS key.
87+
*/
88+
public Builder withKmsKeyId(String kmsKeyId) {
89+
this.kmsKeyId = kmsKeyId;
90+
return this;
91+
}
92+
8193
public DownloadRequest build() {
8294
return new DownloadRequest(this);
8395
}

blob/blob-client/src/main/java/com/salesforce/multicloudj/blob/driver/UploadRequest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ public class UploadRequest {
3131
* (Optional parameter) The map of tagName->tagValue to be associated with the blob
3232
*/
3333
private final Map<String, String> tags;
34+
/**
35+
* (Optional parameter) The KMS key ID or ARN to use for server-side encryption
36+
*/
37+
private final String kmsKeyId;
3438

3539
private UploadRequest(Builder builder) {
3640
this.key = builder.key;
3741
this.contentLength = builder.contentLength;
3842
this.metadata = builder.metadata;
3943
this.tags = builder.tags;
44+
this.kmsKeyId = builder.kmsKeyId;
4045
}
4146

4247
public Map<String, String> getMetadata() {
@@ -52,6 +57,7 @@ public static class Builder {
5257
private long contentLength;
5358
private Map<String, String> metadata = Collections.emptyMap();
5459
private Map<String, String> tags = Collections.emptyMap();
60+
private String kmsKeyId;
5561

5662
public Builder withKey(String key) {
5763
this.key = key;
@@ -73,6 +79,11 @@ public Builder withTags(Map<String, String> tags) {
7379
return this;
7480
}
7581

82+
public Builder withKmsKeyId(String kmsKeyId) {
83+
this.kmsKeyId = kmsKeyId;
84+
return this;
85+
}
86+
7687
public UploadRequest build() {
7788
return new UploadRequest(this);
7889
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.salesforce.multicloudj.blob.driver;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertNull;
8+
9+
public class DownloadRequestTest {
10+
11+
@Test
12+
void testBuilder_WithAllFields() {
13+
// Given
14+
String key = "test-key";
15+
String versionId = "v1";
16+
Long start = 0L;
17+
Long end = 100L;
18+
String kmsKeyId = "arn:aws:kms:us-east-1:123456789012:key/test-key-id";
19+
20+
// When
21+
DownloadRequest request = DownloadRequest.builder()
22+
.withKey(key)
23+
.withVersionId(versionId)
24+
.withRange(start, end)
25+
.withKmsKeyId(kmsKeyId)
26+
.build();
27+
28+
// Then
29+
assertEquals(key, request.getKey());
30+
assertEquals(versionId, request.getVersionId());
31+
assertEquals(start, request.getStart());
32+
assertEquals(end, request.getEnd());
33+
assertEquals(kmsKeyId, request.getKmsKeyId());
34+
}
35+
36+
@Test
37+
void testBuilder_WithoutKmsKeyId() {
38+
// Given
39+
String key = "test-key";
40+
41+
// When
42+
DownloadRequest request = DownloadRequest.builder()
43+
.withKey(key)
44+
.build();
45+
46+
// Then
47+
assertEquals(key, request.getKey());
48+
assertNull(request.getKmsKeyId());
49+
}
50+
51+
@Test
52+
void testBuilder_WithEmptyKmsKeyId() {
53+
// Given
54+
String key = "test-key";
55+
String kmsKeyId = "";
56+
57+
// When
58+
DownloadRequest request = DownloadRequest.builder()
59+
.withKey(key)
60+
.withKmsKeyId(kmsKeyId)
61+
.build();
62+
63+
// Then
64+
assertEquals(key, request.getKey());
65+
assertEquals(kmsKeyId, request.getKmsKeyId());
66+
}
67+
68+
@Test
69+
void testBuilder_MinimalFields() {
70+
// Given
71+
String key = "test-key";
72+
73+
// When
74+
DownloadRequest request = DownloadRequest.builder()
75+
.withKey(key)
76+
.build();
77+
78+
// Then
79+
assertNotNull(request);
80+
assertEquals(key, request.getKey());
81+
}
82+
83+
@Test
84+
void testBuilder_WithRange() {
85+
// Given
86+
String key = "test-key";
87+
Long start = 100L;
88+
Long end = 200L;
89+
90+
// When
91+
DownloadRequest request = DownloadRequest.builder()
92+
.withKey(key)
93+
.withRange(start, end)
94+
.build();
95+
96+
// Then
97+
assertEquals(key, request.getKey());
98+
assertEquals(start, request.getStart());
99+
assertEquals(end, request.getEnd());
100+
}
101+
}

0 commit comments

Comments
 (0)