Skip to content

Commit 5b982e5

Browse files
authored
Add support for presigned DeleteObject (#4313)
* Add support for presigned DeleteObject * Review comments - Add proper annotations - Add equals and hashCode
1 parent 5aa3ff3 commit 5b982e5

File tree

7 files changed

+461
-9
lines changed

7 files changed

+461
-9
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "Amazon S3",
4+
"contributor": "",
5+
"description": "Add support for presigned `DeleteObject` in `S3Presigner`."
6+
}

services/s3/src/it/java/software/amazon/awssdk/services/s3/S3PresignerIntegrationTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import software.amazon.awssdk.services.s3.presigner.model.PresignedAbortMultipartUploadRequest;
5151
import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest;
5252
import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest;
53+
import software.amazon.awssdk.services.s3.presigner.model.PresignedDeleteObjectRequest;
5354
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
5455
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
5556
import software.amazon.awssdk.services.s3.presigner.model.PresignedUploadPartRequest;
@@ -200,6 +201,38 @@ public void getObject_PresignedHttpRequestCanBeInvokedDirectlyBySdk() throws IOE
200201
}
201202
}
202203

204+
@Test
205+
public void deleteObject_PresignedHttpRequestCanBeInvokedDirectlyBySdk() throws IOException {
206+
String objectKey = generateRandomObjectKey();
207+
S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class,
208+
() -> client.deleteObject(r -> r.bucket(testBucket).key(objectKey)));
209+
client.putObject(r -> r.bucket(testBucket).key(objectKey), RequestBody.fromString("DeleteObjectPresignRequestTest"));
210+
211+
PresignedDeleteObjectRequest presigned =
212+
presigner.presignDeleteObject(r -> r.signatureDuration(Duration.ofMinutes(5))
213+
.deleteObjectRequest(delo -> delo.bucket(testBucket)
214+
.key(testGetObjectKey)
215+
.requestPayer(RequestPayer.REQUESTER)));
216+
217+
assertThat(presigned.isBrowserExecutable()).isFalse();
218+
219+
SdkHttpClient httpClient = ApacheHttpClient.builder().build(); // or UrlConnectionHttpClient.builder().build()
220+
221+
ContentStreamProvider requestPayload = presigned.signedPayload()
222+
.map(SdkBytes::asContentStreamProvider)
223+
.orElse(null);
224+
225+
HttpExecuteRequest request = HttpExecuteRequest.builder()
226+
.request(presigned.httpRequest())
227+
.contentStreamProvider(requestPayload)
228+
.build();
229+
230+
HttpExecuteResponse response = httpClient.prepareRequest(request).call();
231+
232+
assertThat(response.responseBody()).isEmpty();
233+
assertThat(response.httpResponse().statusCode()).isEqualTo(204);
234+
}
235+
203236
@Test
204237
public void putObject_PresignedHttpRequestCanBeInvokedDirectlyBySdk() throws IOException {
205238
String objectKey = generateRandomObjectKey();

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/signing/DefaultS3Presigner.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,20 @@
7474
import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest;
7575
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
7676
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
77+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
7778
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
7879
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
7980
import software.amazon.awssdk.services.s3.model.UploadPartRequest;
8081
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
8182
import software.amazon.awssdk.services.s3.presigner.model.AbortMultipartUploadPresignRequest;
8283
import software.amazon.awssdk.services.s3.presigner.model.CompleteMultipartUploadPresignRequest;
8384
import software.amazon.awssdk.services.s3.presigner.model.CreateMultipartUploadPresignRequest;
85+
import software.amazon.awssdk.services.s3.presigner.model.DeleteObjectPresignRequest;
8486
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
8587
import software.amazon.awssdk.services.s3.presigner.model.PresignedAbortMultipartUploadRequest;
8688
import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest;
8789
import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest;
90+
import software.amazon.awssdk.services.s3.presigner.model.PresignedDeleteObjectRequest;
8891
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
8992
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
9093
import software.amazon.awssdk.services.s3.presigner.model.PresignedUploadPartRequest;
@@ -93,6 +96,7 @@
9396
import software.amazon.awssdk.services.s3.transform.AbortMultipartUploadRequestMarshaller;
9497
import software.amazon.awssdk.services.s3.transform.CompleteMultipartUploadRequestMarshaller;
9598
import software.amazon.awssdk.services.s3.transform.CreateMultipartUploadRequestMarshaller;
99+
import software.amazon.awssdk.services.s3.transform.DeleteObjectRequestMarshaller;
96100
import software.amazon.awssdk.services.s3.transform.GetObjectRequestMarshaller;
97101
import software.amazon.awssdk.services.s3.transform.PutObjectRequestMarshaller;
98102
import software.amazon.awssdk.services.s3.transform.UploadPartRequestMarshaller;
@@ -118,6 +122,7 @@ public final class DefaultS3Presigner extends DefaultSdkPresigner implements S3P
118122
private final PutObjectRequestMarshaller putObjectRequestMarshaller;
119123
private final CreateMultipartUploadRequestMarshaller createMultipartUploadRequestMarshaller;
120124
private final UploadPartRequestMarshaller uploadPartRequestMarshaller;
125+
private final DeleteObjectRequestMarshaller deleteObjectRequestMarshaller;
121126
private final CompleteMultipartUploadRequestMarshaller completeMultipartUploadRequestMarshaller;
122127
private final AbortMultipartUploadRequestMarshaller abortMultipartUploadRequestMarshaller;
123128
private final SdkClientConfiguration clientConfiguration;
@@ -172,6 +177,9 @@ private DefaultS3Presigner(Builder b) {
172177
// Copied from DefaultS3Client#uploadPart
173178
this.uploadPartRequestMarshaller = new UploadPartRequestMarshaller(protocolFactory);
174179

180+
// Copied from DefaultS3Client#deleteObject
181+
this.deleteObjectRequestMarshaller = new DeleteObjectRequestMarshaller(protocolFactory);
182+
175183
// Copied from DefaultS3Client#completeMultipartUpload
176184
this.completeMultipartUploadRequestMarshaller = new CompleteMultipartUploadRequestMarshaller(protocolFactory);
177185

@@ -247,6 +255,17 @@ public PresignedPutObjectRequest presignPutObject(PutObjectPresignRequest reques
247255
.build();
248256
}
249257

258+
@Override
259+
public PresignedDeleteObjectRequest presignDeleteObject(DeleteObjectPresignRequest request) {
260+
return presign(PresignedDeleteObjectRequest.builder(),
261+
request,
262+
request.deleteObjectRequest(),
263+
DeleteObjectRequest.class,
264+
deleteObjectRequestMarshaller::marshall,
265+
"DeleteObject")
266+
.build();
267+
}
268+
250269
@Override
251270
public PresignedCreateMultipartUploadRequest presignCreateMultipartUpload(CreateMultipartUploadPresignRequest request) {
252271
return presign(PresignedCreateMultipartUploadRequest.builder(),

services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/S3Presigner.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,19 @@
3535
import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest;
3636
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
3737
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
38+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
3839
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
3940
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
4041
import software.amazon.awssdk.services.s3.model.UploadPartRequest;
4142
import software.amazon.awssdk.services.s3.presigner.model.AbortMultipartUploadPresignRequest;
4243
import software.amazon.awssdk.services.s3.presigner.model.CompleteMultipartUploadPresignRequest;
4344
import software.amazon.awssdk.services.s3.presigner.model.CreateMultipartUploadPresignRequest;
45+
import software.amazon.awssdk.services.s3.presigner.model.DeleteObjectPresignRequest;
4446
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
4547
import software.amazon.awssdk.services.s3.presigner.model.PresignedAbortMultipartUploadRequest;
4648
import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest;
4749
import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest;
50+
import software.amazon.awssdk.services.s3.presigner.model.PresignedDeleteObjectRequest;
4851
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
4952
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
5053
import software.amazon.awssdk.services.s3.presigner.model.PresignedUploadPartRequest;
@@ -339,6 +342,50 @@ default PresignedPutObjectRequest presignPutObject(Consumer<PutObjectPresignRequ
339342
return presignPutObject(builder.build());
340343
}
341344

345+
/**
346+
* Presign a {@link DeleteObjectRequest} so that it can be executed at a later time without requiring additional
347+
* signing or authentication.
348+
* <p>
349+
* <b>Example Usage</b>
350+
*
351+
* <pre>
352+
* {@code
353+
* S3Presigner presigner = ...;
354+
*
355+
* // Create a DeleteObjectRequest to be pre-signed
356+
* DeleteObjectRequest deleteObjectRequest = ...;
357+
*
358+
* // Create a PutObjectPresignRequest to specify the signature duration
359+
* DeleteObjectPresignRequest deleteObjectPresignRequest =
360+
* DeleteObjectPresignRequest.builder()
361+
* .signatureDuration(Duration.ofMinutes(10))
362+
* .deleteObjectRequest(deleteObjectRequest)
363+
* .build();
364+
*
365+
* // Generate the presigned request
366+
* PresignedDeleteObjectRequest presignedDeleteObjectRequest =
367+
* presigner.presignDeleteObject(deleteObjectPresignRequest);
368+
* }
369+
* </pre>
370+
*/
371+
PresignedDeleteObjectRequest presignDeleteObject(DeleteObjectPresignRequest request);
372+
373+
/**
374+
* Presign a {@link DeleteObjectRequest} so that it can be executed at a later time without requiring additional
375+
* signing or authentication.
376+
* <p>
377+
* This is a shorter method of invoking {@link #presignDeleteObject(DeleteObjectPresignRequest)} without needing
378+
* to call {@code DeleteObjectPresignRequest.builder()} or {@code .build()}.
379+
*
380+
* @see #presignDeleteObject(PresignedDeleteObjectRequest)
381+
*/
382+
default PresignedDeleteObjectRequest presignDeleteObject(Consumer<DeleteObjectPresignRequest.Builder> request) {
383+
DeleteObjectPresignRequest.Builder builder = DeleteObjectPresignRequest.builder();
384+
request.accept(builder);
385+
return presignDeleteObject(builder.build());
386+
}
387+
388+
342389
/**
343390
* Presign a {@link CreateMultipartUploadRequest} so that it can be executed at a later time without requiring additional
344391
* signing or authentication.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.presigner.model;
17+
18+
import java.time.Duration;
19+
import java.util.function.Consumer;
20+
import software.amazon.awssdk.annotations.Immutable;
21+
import software.amazon.awssdk.annotations.NotThreadSafe;
22+
import software.amazon.awssdk.annotations.SdkInternalApi;
23+
import software.amazon.awssdk.annotations.SdkPublicApi;
24+
import software.amazon.awssdk.annotations.ThreadSafe;
25+
import software.amazon.awssdk.awscore.presigner.PresignRequest;
26+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
27+
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
28+
import software.amazon.awssdk.utils.Validate;
29+
import software.amazon.awssdk.utils.builder.CopyableBuilder;
30+
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
31+
32+
/**
33+
* A request to pre-sign a {@link DeleteObjectRequest} so that it can be executed at a later time without requiring additional
34+
* signing or authentication.
35+
*
36+
* @see S3Presigner#presignDeleteObject(DeleteObjectPresignRequest
37+
* @see #builder()
38+
*/
39+
@SdkPublicApi
40+
@Immutable
41+
@ThreadSafe
42+
public final class DeleteObjectPresignRequest extends PresignRequest
43+
implements ToCopyableBuilder<DeleteObjectPresignRequest.Builder, DeleteObjectPresignRequest> {
44+
private final DeleteObjectRequest deleteObjectRequest;
45+
46+
protected DeleteObjectPresignRequest(DefaultBuilder builder) {
47+
super(builder);
48+
this.deleteObjectRequest = Validate.notNull(builder.deleteObjectRequest, "deleteObjectRequest");
49+
}
50+
51+
/**
52+
* Retrieve the {@link DeleteObjectRequest} that should be presigned.
53+
*/
54+
public DeleteObjectRequest deleteObjectRequest() {
55+
return deleteObjectRequest;
56+
}
57+
58+
@Override
59+
public Builder toBuilder() {
60+
return new DefaultBuilder(this);
61+
}
62+
63+
/**
64+
* Create a builder that can be used to create a {@link DeleteObjectPresignRequest}.
65+
*
66+
* @see S3Presigner#presignDeleteObject(DeleteObjectPresignRequest)
67+
*/
68+
public static Builder builder() {
69+
return new DefaultBuilder();
70+
}
71+
72+
@Override
73+
public boolean equals(Object o) {
74+
if (this == o) {
75+
return true;
76+
}
77+
if (o == null || getClass() != o.getClass()) {
78+
return false;
79+
}
80+
if (!super.equals(o)) {
81+
return false;
82+
}
83+
84+
DeleteObjectPresignRequest that = (DeleteObjectPresignRequest) o;
85+
86+
return deleteObjectRequest.equals(that.deleteObjectRequest);
87+
}
88+
89+
@Override
90+
public int hashCode() {
91+
int result = super.hashCode();
92+
result = 31 * result + deleteObjectRequest.hashCode();
93+
return result;
94+
}
95+
96+
@SdkPublicApi
97+
@NotThreadSafe
98+
public interface Builder extends PresignRequest.Builder,
99+
CopyableBuilder<DeleteObjectPresignRequest.Builder, DeleteObjectPresignRequest> {
100+
Builder deleteObjectRequest(DeleteObjectRequest deleteObjectRequest);
101+
102+
default Builder deleteObjectRequest(Consumer<DeleteObjectRequest.Builder> deleteObjectRequest) {
103+
DeleteObjectRequest.Builder builder = DeleteObjectRequest.builder();
104+
deleteObjectRequest.accept(builder);
105+
return deleteObjectRequest(builder.build());
106+
}
107+
108+
@Override
109+
Builder signatureDuration(Duration signatureDuration);
110+
111+
@Override
112+
DeleteObjectPresignRequest build();
113+
}
114+
115+
@SdkInternalApi
116+
private static final class DefaultBuilder extends PresignRequest.DefaultBuilder<DefaultBuilder> implements Builder {
117+
private DeleteObjectRequest deleteObjectRequest;
118+
119+
private DefaultBuilder() {
120+
}
121+
122+
private DefaultBuilder(DeleteObjectPresignRequest deleteObjectPresignRequest) {
123+
super(deleteObjectPresignRequest);
124+
this.deleteObjectRequest = deleteObjectPresignRequest.deleteObjectRequest;
125+
}
126+
127+
@Override
128+
public Builder deleteObjectRequest(DeleteObjectRequest deleteObjectRequest) {
129+
this.deleteObjectRequest = deleteObjectRequest;
130+
return this;
131+
}
132+
133+
@Override
134+
public DeleteObjectPresignRequest build() {
135+
return new DeleteObjectPresignRequest(this);
136+
}
137+
}
138+
}

0 commit comments

Comments
 (0)