Skip to content

Commit 7b43470

Browse files
idivanshuDivanshu PrajapatMalaydewangan09
authored
feat(aws-s3): add SSE-KMS support for MOVE action in S3 Trigger & Copy (#667)
* feat(aws-s3): add KMS support for moveTo action with SSE-KMS parameters Adds support for `serverSideEncryption` and `kmsKeyId` in S3 MOVE actions inside the Trigger and Copy operations. This enables workflows to move files into buckets that enforce encryption policies requiring SSE-KMS. Includes schema updates, parameter handling, and correct wiring inside S3Service performAction(). * test(aws-s3): remove @PluginProperty usage and add SSE-KMS integration tests - Removed deprecated @PluginProperty annotations from Copy.CopyObject fields - Added runWithServerSideEncryption and runWithKmsKey tests to CopyTest * Update src/main/java/io/kestra/plugin/aws/s3/Copy.java * Update src/main/java/io/kestra/plugin/aws/s3/Copy.java Co-authored-by: Malay Dewangan <66718045+Malaydewangan09@users.noreply.github.com> * refactor(aws-s3): replace SDK SSE enum with plugin enum * refactor(s3): replace switch-case with direct ServerSideEncryption.valueOf() mapping * chore(s3): remove unused toHeaderValue() and clean up SSE enum --------- Co-authored-by: Divanshu Prajapat <honex@Divanshus-MacBook-Air.local> Co-authored-by: Malay Dewangan <66718045+Malaydewangan09@users.noreply.github.com>
1 parent f089ab7 commit 7b43470

File tree

3 files changed

+96
-1
lines changed

3 files changed

+96
-1
lines changed

src/main/java/io/kestra/plugin/aws/s3/Copy.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
package io.kestra.plugin.aws.s3;
2-
32
import io.kestra.core.models.annotations.Example;
43
import io.kestra.core.models.annotations.Plugin;
54
import io.kestra.core.models.annotations.PluginProperty;
@@ -14,6 +13,7 @@
1413
import software.amazon.awssdk.services.s3.S3Client;
1514
import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
1615
import software.amazon.awssdk.services.s3.model.CopyObjectResponse;
16+
import io.kestra.plugin.aws.s3.models.S3ServerSideEncryption;
1717

1818
@SuperBuilder
1919
@ToString
@@ -79,6 +79,20 @@ public Output run(RunContext runContext) throws Exception {
7979
builder.sourceVersionId(runContext.render(this.from.versionId).as(String.class).orElseThrow());
8080
}
8181

82+
if (this.to != null && this.to.serverSideEncryption != null) {
83+
S3ServerSideEncryption rSse = runContext
84+
.render(this.to.serverSideEncryption)
85+
.as(S3ServerSideEncryption.class)
86+
.orElse(null);
87+
88+
if (rSse != null && rSse != S3ServerSideEncryption.NONE) {
89+
builder.serverSideEncryption(
90+
software.amazon.awssdk.services.s3.model.ServerSideEncryption.valueOf(rSse.name())
91+
);
92+
}
93+
}
94+
95+
8296
CopyObjectRequest request = builder.build();
8397
CopyObjectResponse response = client.copyObject(request);
8498

@@ -126,6 +140,17 @@ public static class CopyObject {
126140
)
127141
@NotNull
128142
Property<String> key;
143+
144+
@Schema(
145+
title = "Server side encryption to apply to the target object.",
146+
description = "Example: AES256 or AWS_KMS"
147+
)
148+
private Property<S3ServerSideEncryption> serverSideEncryption;
149+
150+
@Schema(
151+
title = "KMS Key ARN or Key ID to use when server side encryption is AWS_KMS"
152+
)
153+
private Property<String> kmsKeyId;
129154
}
130155

131156
@SuperBuilder(toBuilder = true)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.kestra.plugin.aws.s3.models;
2+
3+
public enum S3ServerSideEncryption {
4+
NONE,
5+
AES256,
6+
AWS_KMS;
7+
}

src/test/java/io/kestra/plugin/aws/s3/CopyTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,67 @@ void run() throws Exception {
6060
void delete() throws Exception {
6161
this.run(true);
6262
}
63+
64+
@Test
65+
void runWithServerSideEncryption() throws Exception {
66+
this.createBucket();
67+
68+
String upload = upload("/tasks/s3/" + IdUtils.create() + "/sub");
69+
String move = upload("/tasks/s3/" + IdUtils.create() + "/sub");
70+
71+
Copy task = Copy.builder()
72+
.id(CopyTest.class.getSimpleName())
73+
.type(Copy.class.getName())
74+
.endpointOverride(Property.ofValue(localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString()))
75+
.accessKeyId(Property.ofValue(localstack.getAccessKey()))
76+
.secretKeyId(Property.ofValue(localstack.getSecretKey()))
77+
.region(Property.ofValue(localstack.getRegion()))
78+
.from(Copy.CopyObjectFrom.builder()
79+
.bucket(Property.ofValue(this.BUCKET))
80+
.key(Property.ofValue(upload))
81+
.build()
82+
)
83+
.to(Copy.CopyObject.builder()
84+
.key(Property.ofValue(move))
85+
.serverSideEncryption(Property.ofValue(io.kestra.plugin.aws.s3.models.S3ServerSideEncryption.AES256))
86+
.build()
87+
)
88+
.delete(Property.ofValue(false))
89+
.build();
90+
91+
Copy.Output run = task.run(runContext(task));
92+
assertThat(run.getKey(), is(move));
93+
}
94+
95+
@Test
96+
void runWithKmsKey() throws Exception {
97+
this.createBucket();
98+
99+
String upload = upload("/tasks/s3/" + IdUtils.create() + "/sub");
100+
String move = upload("/tasks/s3/" + IdUtils.create() + "/sub");
101+
102+
Copy task = Copy.builder()
103+
.id(CopyTest.class.getSimpleName())
104+
.type(Copy.class.getName())
105+
.endpointOverride(Property.ofValue(localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString()))
106+
.accessKeyId(Property.ofValue(localstack.getAccessKey()))
107+
.secretKeyId(Property.ofValue(localstack.getSecretKey()))
108+
.region(Property.ofValue(localstack.getRegion()))
109+
.from(Copy.CopyObjectFrom.builder()
110+
.bucket(Property.ofValue(this.BUCKET))
111+
.key(Property.ofValue(upload))
112+
.build()
113+
)
114+
.to(Copy.CopyObject.builder()
115+
.key(Property.ofValue(move))
116+
.serverSideEncryption(Property.ofValue(io.kestra.plugin.aws.s3.models.S3ServerSideEncryption.AWS_KMS))
117+
.kmsKeyId(Property.ofValue("arn:aws:kms:us-east-1:000000000000:key/test-kms"))
118+
.build()
119+
)
120+
.delete(Property.ofValue(false))
121+
.build();
122+
123+
Copy.Output run = task.run(runContext(task));
124+
assertThat(run.getKey(), is(move));
125+
}
63126
}

0 commit comments

Comments
 (0)