Skip to content

Commit 37e772f

Browse files
kessplastexastony
andauthored
feat: allow configuration of instruction file client, add new top-level client options, disable wrapped multipart upload (#387)
* update crt to latest * feat: introduce instruction file config * fix ranged gets * start to block MPU downloads * fix(InstructionFileConfig): allow for None * fix(S3AsyncClientBuilder): fail on SDK Multipart Options --------- Co-authored-by: texastony <[email protected]>
1 parent 5e03842 commit 37e772f

16 files changed

+621
-36
lines changed

cfn/S3EC-GitHub-CF-Template.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ Resources:
7676
PolicyDocument:
7777
Version: 2012-10-17
7878
Statement:
79+
- Effect: Allow
80+
Action:
81+
- 's3:ListBucket'
82+
Resource:
83+
- !GetAtt S3ECGitHubTestS3Bucket.Arn
7984
- Effect: Allow
8085
Action:
8186
- 's3:PutObject'

pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
<dependency>
5757
<groupId>software.amazon.awssdk</groupId>
5858
<artifactId>bom</artifactId>
59-
<version>2.20.38</version>
59+
<version>2.28.28</version>
6060
<optional>true</optional>
6161
<type>pom</type>
6262
<scope>import</scope>
@@ -68,22 +68,22 @@
6868
<dependency>
6969
<groupId>software.amazon.awssdk</groupId>
7070
<artifactId>s3</artifactId>
71-
<version>2.20.38</version>
71+
<version>2.28.28</version>
7272
</dependency>
7373

7474
<dependency>
7575
<groupId>software.amazon.awssdk</groupId>
7676
<artifactId>kms</artifactId>
77-
<version>2.20.38</version>
77+
<version>2.28.28</version>
7878
<optional>true</optional>
7979
</dependency>
8080

8181
<!-- Used when enableMultipartPutObject is configured -->
8282
<dependency>
8383
<groupId>software.amazon.awssdk.crt</groupId>
8484
<artifactId>aws-crt</artifactId>
85-
<version>0.29.24</version>
8685
<optional>true</optional>
86+
<version>0.31.3</version>
8787
</dependency>
8888

8989
<dependency>
@@ -164,7 +164,7 @@
164164
<dependency>
165165
<groupId>software.amazon.awssdk</groupId>
166166
<artifactId>sts</artifactId>
167-
<version>2.20.38</version>
167+
<version>2.28.28</version>
168168
<optional>true</optional>
169169
<scope>test</scope>
170170
</dependency>

src/main/java/software/amazon/encryption/s3/S3AsyncEncryptionClient.java

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
3131
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
3232
import software.amazon.awssdk.services.s3.model.S3Request;
33+
import software.amazon.awssdk.services.s3.multipart.MultipartConfiguration;
3334
import software.amazon.encryption.s3.internal.GetEncryptedObjectPipeline;
35+
import software.amazon.encryption.s3.internal.InstructionFileConfig;
3436
import software.amazon.encryption.s3.internal.NoRetriesAsyncRequestBody;
3537
import software.amazon.encryption.s3.internal.PutEncryptedObjectPipeline;
3638
import software.amazon.encryption.s3.materials.AesKeyring;
@@ -71,6 +73,7 @@ public class S3AsyncEncryptionClient extends DelegatingS3AsyncClient {
7173
private final boolean _enableDelayedAuthenticationMode;
7274
private final boolean _enableMultipartPutObject;
7375
private final long _bufferSize;
76+
private InstructionFileConfig _instructionFileConfig;
7477

7578
private S3AsyncEncryptionClient(Builder builder) {
7679
super(builder._wrappedClient);
@@ -81,6 +84,7 @@ private S3AsyncEncryptionClient(Builder builder) {
8184
_enableDelayedAuthenticationMode = builder._enableDelayedAuthenticationMode;
8285
_enableMultipartPutObject = builder._enableMultipartPutObject;
8386
_bufferSize = builder._bufferSize;
87+
_instructionFileConfig = builder._instructionFileConfig;
8488
}
8589

8690
/**
@@ -147,16 +151,16 @@ public CompletableFuture<PutObjectResponse> putObject(PutObjectRequest putObject
147151
}
148152

149153
private CompletableFuture<PutObjectResponse> multipartPutObject(PutObjectRequest putObjectRequest, AsyncRequestBody requestBody) {
150-
S3AsyncClient crtClient;
154+
S3AsyncClient mpuClient;
151155
if (_wrappedClient instanceof S3CrtAsyncClient) {
152156
// if the wrappedClient is a CRT, use it
153-
crtClient = _wrappedClient;
157+
mpuClient = _wrappedClient;
154158
} else {
155-
// else create a default one
156-
crtClient = S3AsyncClient.crtCreate();
159+
// else create a default CRT client
160+
mpuClient = S3AsyncClient.crtCreate();
157161
}
158162
PutEncryptedObjectPipeline pipeline = PutEncryptedObjectPipeline.builder()
159-
.s3AsyncClient(crtClient)
163+
.s3AsyncClient(mpuClient)
160164
.cryptoMaterialsManager(_cryptoMaterialsManager)
161165
.secureRandom(_secureRandom)
162166
.build();
@@ -198,6 +202,7 @@ public <T> CompletableFuture<T> getObject(GetObjectRequest getObjectRequest,
198202
.enableLegacyUnauthenticatedModes(_enableLegacyUnauthenticatedModes)
199203
.enableDelayedAuthentication(_enableDelayedAuthenticationMode)
200204
.bufferSize(_bufferSize)
205+
.instructionFileConfig(_instructionFileConfig)
201206
.build();
202207

203208
return pipeline.getObject(getObjectRequest, asyncResponseTransformer);
@@ -257,6 +262,7 @@ public CompletableFuture<DeleteObjectsResponse> deleteObjects(DeleteObjectsReque
257262
@Override
258263
public void close() {
259264
_wrappedClient.close();
265+
_instructionFileConfig.closeClient();
260266
}
261267

262268
// This is very similar to the S3EncryptionClient builder
@@ -275,6 +281,7 @@ public static class Builder implements S3AsyncClientBuilder {
275281
private Provider _cryptoProvider = null;
276282
private SecureRandom _secureRandom = new SecureRandom();
277283
private long _bufferSize = -1L;
284+
private InstructionFileConfig _instructionFileConfig = null;
278285

279286
// generic AwsClient configuration to be shared by default clients
280287
private AwsCredentialsProvider _awsCredentialsProvider = null;
@@ -291,8 +298,12 @@ public static class Builder implements S3AsyncClientBuilder {
291298
private S3Configuration _serviceConfiguration = null;
292299
private Boolean _accelerate = null;
293300
private Boolean _disableMultiRegionAccessPoints = null;
301+
private Boolean _disableS3ExpressSessionAuth = null;
294302
private Boolean _forcePathStyle = null;
295303
private Boolean _useArnRegion = null;
304+
private Boolean _crossRegionAccessEnabled = null;
305+
// private Boolean _multipartEnabled = null;
306+
// private MultipartConfiguration _multipartConfiguration = null;
296307

297308
private Builder() {
298309
}
@@ -518,6 +529,18 @@ public Builder secureRandom(SecureRandom secureRandom) {
518529
return this;
519530
}
520531

532+
/**
533+
* Sets the Instruction File configuration for the S3 Encryption Client.
534+
* The InstructionFileConfig can be used to specify an S3 client to use for retrieval of Instruction files,
535+
* or to disable GetObject requests for the instruction file.
536+
* @param instructionFileConfig
537+
* @return
538+
*/
539+
public Builder instructionFileConfig(InstructionFileConfig instructionFileConfig) {
540+
_instructionFileConfig = instructionFileConfig;
541+
return this;
542+
}
543+
521544
/**
522545
* The credentials provider to use for all inner clients, including KMS, if a KMS key ID is provided.
523546
* Note that if a wrapped client is configured, the wrapped client will take precedence over this option.
@@ -696,6 +719,16 @@ public Builder disableMultiRegionAccessPoints(Boolean disableMultiRegionAccessPo
696719
return this;
697720
}
698721

722+
/**
723+
* Disables this client's usage of Session Auth for S3Express buckets and reverts to using conventional SigV4 for
724+
* those.
725+
*/
726+
@Override
727+
public Builder disableS3ExpressSessionAuth(Boolean disableS3ExpressSessionAuth) {
728+
_disableS3ExpressSessionAuth = disableS3ExpressSessionAuth;
729+
return this;
730+
}
731+
699732
/**
700733
* Forces this client to use path-style addressing for buckets.
701734
*
@@ -719,6 +752,35 @@ public Builder useArnRegion(Boolean useArnRegion) {
719752
return this;
720753
}
721754

755+
/**
756+
* Multipart via the wrapped client is currently NOT supported by the S3 Encryption Client.
757+
* Use the {@link this.enableMultipartPutObject()} option instead for high-level multipart uploads.
758+
* Multipart downloads are currently NOT supported.
759+
*/
760+
@Override
761+
public Builder multipartEnabled(Boolean enabled) {
762+
throw new UnsupportedOperationException("The S3 Encryption Client does not support wrapped clients with automatic multipart enabled.");
763+
}
764+
765+
/**
766+
* Multipart via the wrapped client is currently NOT supported by the S3 Encryption Client.
767+
* Use the {@link this.enableMultipartPutObject()} option instead for high-level multipart uploads.
768+
* Multipart downloads are currently NOT supported.
769+
*/
770+
@Override
771+
public Builder multipartConfiguration(MultipartConfiguration multipartConfiguration) {
772+
throw new UnsupportedOperationException("The S3 Encryption Client does not support wrapped clients with automatic multipart enabled.");
773+
}
774+
775+
/**
776+
* Enables cross-region bucket access for this client
777+
*/
778+
@Override
779+
public Builder crossRegionAccessEnabled(Boolean crossRegionAccessEnabled) {
780+
_crossRegionAccessEnabled = crossRegionAccessEnabled;
781+
return this;
782+
}
783+
722784
/**
723785
* Validates and builds the S3AsyncEncryptionClient according
724786
* to the configuration options passed to the Builder object.
@@ -751,8 +813,21 @@ public S3AsyncEncryptionClient build() {
751813
.serviceConfiguration(_serviceConfiguration)
752814
.accelerate(_accelerate)
753815
.disableMultiRegionAccessPoints(_disableMultiRegionAccessPoints)
816+
.disableS3ExpressSessionAuth(_disableS3ExpressSessionAuth)
754817
.forcePathStyle(_forcePathStyle)
755818
.useArnRegion(_useArnRegion)
819+
.crossRegionAccessEnabled(_crossRegionAccessEnabled)
820+
// If either of these are null, the AWS SDK will throw an exception.
821+
// Since there is no way to set them without an exception being thrown,
822+
// this would always result in an exception.
823+
// .multipartEnabled(_multipartEnabled)
824+
// .multipartConfiguration(_multipartConfiguration)
825+
.build();
826+
}
827+
828+
if (_instructionFileConfig == null) {
829+
_instructionFileConfig = InstructionFileConfig.builder()
830+
.instructionFileAsyncClient(_wrappedClient)
756831
.build();
757832
}
758833

src/main/java/software/amazon/encryption/s3/S3EncryptionClient.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import software.amazon.awssdk.services.s3.model.UploadPartResponse;
4848
import software.amazon.encryption.s3.algorithms.AlgorithmSuite;
4949
import software.amazon.encryption.s3.internal.GetEncryptedObjectPipeline;
50+
import software.amazon.encryption.s3.internal.InstructionFileConfig;
5051
import software.amazon.encryption.s3.internal.MultiFileOutputStream;
5152
import software.amazon.encryption.s3.internal.MultipartUploadObjectPipeline;
5253
import software.amazon.encryption.s3.internal.PutEncryptedObjectPipeline;
@@ -104,6 +105,7 @@ public class S3EncryptionClient extends DelegatingS3Client {
104105
private final boolean _enableMultipartPutObject;
105106
private final MultipartUploadObjectPipeline _multipartPipeline;
106107
private final long _bufferSize;
108+
private final InstructionFileConfig _instructionFileConfig;
107109

108110
private S3EncryptionClient(Builder builder) {
109111
super(builder._wrappedClient);
@@ -116,6 +118,7 @@ private S3EncryptionClient(Builder builder) {
116118
_enableMultipartPutObject = builder._enableMultipartPutObject;
117119
_multipartPipeline = builder._multipartPipeline;
118120
_bufferSize = builder._bufferSize;
121+
_instructionFileConfig = builder._instructionFileConfig;
119122
}
120123

121124
/**
@@ -258,6 +261,7 @@ public <T> T getObject(GetObjectRequest getObjectRequest,
258261
.enableLegacyUnauthenticatedModes(_enableLegacyUnauthenticatedModes)
259262
.enableDelayedAuthentication(_enableDelayedAuthenticationMode)
260263
.bufferSize(_bufferSize)
264+
.instructionFileConfig(_instructionFileConfig)
261265
.build();
262266

263267
try {
@@ -510,6 +514,7 @@ public AbortMultipartUploadResponse abortMultipartUpload(AbortMultipartUploadReq
510514
public void close() {
511515
_wrappedClient.close();
512516
_wrappedAsyncClient.close();
517+
_instructionFileConfig.closeClient();
513518
}
514519

515520
// This is very similar to the S3AsyncEncryptionClient builder
@@ -532,7 +537,7 @@ public static class Builder implements S3BaseClientBuilder<Builder, S3Encryption
532537
private SecureRandom _secureRandom = new SecureRandom();
533538
private boolean _enableLegacyUnauthenticatedModes = false;
534539
private long _bufferSize = -1L;
535-
540+
private InstructionFileConfig _instructionFileConfig = null;
536541
// generic AwsClient configuration to be shared by default clients
537542
private AwsCredentialsProvider _awsCredentialsProvider = null;
538543
private Region _region = null;
@@ -544,8 +549,10 @@ public static class Builder implements S3BaseClientBuilder<Builder, S3Encryption
544549
private S3Configuration _serviceConfiguration = null;
545550
private Boolean _accelerate = null;
546551
private Boolean _disableMultiRegionAccessPoints = null;
552+
private Boolean _disableS3ExpressSessionAuth = null;
547553
private Boolean _forcePathStyle = null;
548554
private Boolean _useArnRegion = null;
555+
private Boolean _crossRegionAccessEnabled = null;
549556
private SdkHttpClient _httpClient = null;
550557
private SdkHttpClient.Builder _httpClientBuilder = null;
551558
private SdkAsyncHttpClient _asyncHttpClient = null;
@@ -786,6 +793,18 @@ public Builder secureRandom(SecureRandom secureRandom) {
786793
return this;
787794
}
788795

796+
/**
797+
* Sets the Instruction File configuration for the S3 Encryption Client.
798+
* The InstructionFileConfig can be used to specify an S3 client to use for retrieval of Instruction files,
799+
* or to disable GetObject requests for the instruction file.
800+
* @param instructionFileConfig
801+
* @return
802+
*/
803+
public Builder instructionFileConfig(InstructionFileConfig instructionFileConfig) {
804+
_instructionFileConfig = instructionFileConfig;
805+
return this;
806+
}
807+
789808
/**
790809
* The credentials provider to use for all inner clients, including KMS, if a KMS key ID is provided.
791810
* Note that if a wrapped client is configured, the wrapped client will take precedence over this option.
@@ -918,6 +937,18 @@ public Builder disableMultiRegionAccessPoints(Boolean disableMultiRegionAccessPo
918937
return this;
919938
}
920939

940+
/**
941+
* Disables this client's usage of Session Auth for S3Express buckets and reverts to using conventional SigV4 for
942+
* those.
943+
*
944+
* @param disableS3ExpressSessionAuth
945+
*/
946+
@Override
947+
public Builder disableS3ExpressSessionAuth(Boolean disableS3ExpressSessionAuth) {
948+
_disableS3ExpressSessionAuth = disableS3ExpressSessionAuth;
949+
return this;
950+
}
951+
921952
/**
922953
* Forces this client to use path-style addressing for buckets.
923954
*
@@ -941,6 +972,17 @@ public Builder useArnRegion(Boolean useArnRegion) {
941972
return this;
942973
}
943974

975+
/**
976+
* Enables cross-region bucket access for this client
977+
*
978+
* @param crossRegionAccessEnabled
979+
*/
980+
@Override
981+
public Builder crossRegionAccessEnabled(Boolean crossRegionAccessEnabled) {
982+
_crossRegionAccessEnabled = crossRegionAccessEnabled;
983+
return this;
984+
}
985+
944986
/**
945987
* Sets the {@link SdkHttpClient} that the SDK service client will use to make HTTP calls. This HTTP client may be
946988
* shared between multiple SDK service clients to share a common connection pool. To create a client you must use an
@@ -1052,6 +1094,8 @@ public S3EncryptionClient build() {
10521094
.useArnRegion(_useArnRegion)
10531095
.httpClient(_httpClient)
10541096
.httpClientBuilder(_httpClientBuilder)
1097+
.disableS3ExpressSessionAuth(_disableS3ExpressSessionAuth)
1098+
.crossRegionAccessEnabled(_crossRegionAccessEnabled)
10551099
.build();
10561100
}
10571101

@@ -1070,6 +1114,14 @@ public S3EncryptionClient build() {
10701114
.useArnRegion(_useArnRegion)
10711115
.httpClient(_asyncHttpClient)
10721116
.httpClientBuilder(_asyncHttpClientBuilder)
1117+
.disableS3ExpressSessionAuth(_disableS3ExpressSessionAuth)
1118+
.crossRegionAccessEnabled(_crossRegionAccessEnabled)
1119+
.build();
1120+
}
1121+
1122+
if (_instructionFileConfig == null) {
1123+
_instructionFileConfig = InstructionFileConfig.builder()
1124+
.instructionFileClient(_wrappedClient)
10731125
.build();
10741126
}
10751127

0 commit comments

Comments
 (0)