2020import java .util .HashSet ;
2121import java .util .List ;
2222import java .util .Map ;
23+ import java .util .Objects ;
2324import java .util .Set ;
2425import software .amazon .awssdk .annotations .SdkInternalApi ;
2526import software .amazon .awssdk .core .SdkField ;
2627import software .amazon .awssdk .core .SdkPojo ;
28+ import software .amazon .awssdk .core .exception .SdkClientException ;
2729import software .amazon .awssdk .services .s3 .model .AbortMultipartUploadRequest ;
2830import software .amazon .awssdk .services .s3 .model .ChecksumType ;
2931import software .amazon .awssdk .services .s3 .model .CompleteMultipartUploadRequest ;
@@ -53,12 +55,104 @@ public final class SdkPojoConversionUtils {
5355 new HashSet <>(Arrays .asList ("ChecksumSHA1" , "ChecksumSHA256" , "ContentMD5" , "ChecksumCRC32C" , "ChecksumCRC32" ,
5456 "ChecksumCRC64NVME" , "ContentLength" ));
5557
58+ private static final Set <String > PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS = new HashSet <>(Arrays .asList (
59+ "ACL" ,
60+ "Bucket" ,
61+ "CacheControl" ,
62+ "ContentDisposition" ,
63+ "ContentEncoding" ,
64+ "ContentLanguage" ,
65+ "ContentLength" ,
66+ "ContentMD5" ,
67+ "ContentType" ,
68+ "ChecksumAlgorithm" ,
69+ "ChecksumCRC32" ,
70+ "ChecksumCRC32C" ,
71+ "ChecksumCRC64NVME" ,
72+ "ChecksumSHA1" ,
73+ "ChecksumSHA256" ,
74+ "Expires" ,
75+ "IfMatch" ,
76+ "IfNoneMatch" ,
77+ "GrantFullControl" ,
78+ "GrantRead" ,
79+ "GrantReadACP" ,
80+ "GrantWriteACP" ,
81+ "Key" ,
82+ "WriteOffsetBytes" ,
83+ "Metadata" ,
84+ "ServerSideEncryption" ,
85+ "StorageClass" ,
86+ "WebsiteRedirectLocation" ,
87+ "SSECustomerAlgorithm" ,
88+ "SSECustomerKey" ,
89+ "SSECustomerKeyMD5" ,
90+ "SSEKMSKeyId" ,
91+ "SSEKMSEncryptionContext" ,
92+ "BucketKeyEnabled" ,
93+ "RequestPayer" ,
94+ "Tagging" ,
95+ "ObjectLockMode" ,
96+ "ObjectLockRetainUntilDate" ,
97+ "ObjectLockLegalHoldStatus" ,
98+ "ExpectedBucketOwner"
99+ ));
100+
101+ private static final Set <String > COPY_OBJECT_TO_COPY_OBJECT_ALLOWED_FIELDS = new HashSet <>(Arrays .asList (
102+ "ACL" ,
103+ "CacheControl" ,
104+ "ChecksumAlgorithm" ,
105+ "ContentDisposition" ,
106+ "ContentEncoding" ,
107+ "ContentLanguage" ,
108+ "ContentType" ,
109+ "CopySource" ,
110+ "CopySourceIfMatch" ,
111+ "CopySourceIfModifiedSince" ,
112+ "CopySourceIfNoneMatch" ,
113+ "CopySourceIfUnmodifiedSince" ,
114+ "CopySourceSSECustomerAlgorithm" ,
115+ "CopySourceSSECustomerKey" ,
116+ "CopySourceSSECustomerKeyMD5" ,
117+ "DestinationBucket" ,
118+ "DestinationKey" ,
119+ "Expires" ,
120+ "GrantFullControl" ,
121+ "GrantRead" ,
122+ "GrantReadACP" ,
123+ "GrantWriteACP" ,
124+ "Metadata" ,
125+ "MetadataDirective" ,
126+ "TaggingDirective" ,
127+ "ServerSideEncryption" ,
128+ "SourceBucket" ,
129+ "SourceKey" ,
130+ "SourceVersionId" ,
131+ "StorageClass" ,
132+ "WebsiteRedirectLocation" ,
133+ "SSECustomerAlgorithm" ,
134+ "SSECustomerKey" ,
135+ "SSECustomerKeyMD5" ,
136+ "SSEKMSKeyId" ,
137+ "SSEKMSEncryptionContext" ,
138+ "BucketKeyEnabled" ,
139+ "RequestPayer" ,
140+ "Tagging" ,
141+ "ObjectLockMode" ,
142+ "ObjectLockRetainUntilDate" ,
143+ "ObjectLockLegalHoldStatus" ,
144+ "ExpectedBucketOwner" ,
145+ "ExpectedSourceBucketOwner"
146+ ));
147+
148+
56149 private SdkPojoConversionUtils () {
57150 }
58151
59152 public static UploadPartRequest toUploadPartRequest (PutObjectRequest putObjectRequest , int partNumber , String uploadId ) {
60153
61154 UploadPartRequest .Builder builder = UploadPartRequest .builder ();
155+ validateRequestFields (putObjectRequest , builder .build (), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
62156 setSdkFields (builder , putObjectRequest , PUT_OBJECT_REQUEST_TO_UPLOAD_PART_FIELDS_TO_IGNORE );
63157 return builder .uploadId (uploadId ).partNumber (partNumber ).build ();
64158 }
@@ -67,6 +161,7 @@ public static CompleteMultipartUploadRequest toCompleteMultipartUploadRequest(Pu
67161 String uploadId , CompletedPart [] parts ,
68162 long contentLength ) {
69163 CompleteMultipartUploadRequest .Builder builder = CompleteMultipartUploadRequest .builder ();
164+ validateRequestFields (putObjectRequest , builder .build (), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
70165 setSdkFields (builder , putObjectRequest );
71166
72167 builder .mpuObjectSize (contentLength );
@@ -81,6 +176,7 @@ public static CompleteMultipartUploadRequest toCompleteMultipartUploadRequest(Pu
81176 public static CreateMultipartUploadRequest toCreateMultipartUploadRequest (PutObjectRequest putObjectRequest ) {
82177
83178 CreateMultipartUploadRequest .Builder builder = CreateMultipartUploadRequest .builder ();
179+ validateRequestFields (putObjectRequest , builder .build (), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
84180 setSdkFields (builder , putObjectRequest );
85181
86182 if (S3ChecksumUtils .checksumValueSpecified (putObjectRequest )) {
@@ -130,29 +226,14 @@ public static CompletedPart toCompletedPart(Part part) {
130226
131227 public static ListPartsRequest toListPartsRequest (String uploadId , PutObjectRequest putObjectRequest ) {
132228 ListPartsRequest .Builder builder = ListPartsRequest .builder ();
229+ validateRequestFields (putObjectRequest , builder .build (), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
133230 setSdkFields (builder , putObjectRequest );
134231 return builder .uploadId (uploadId ).build ();
135232 }
136233
137- private static void setSdkFields (SdkPojo targetBuilder , SdkPojo sourceObject ) {
138- setSdkFields (targetBuilder , sourceObject , new HashSet <>());
139- }
140-
141- private static void setSdkFields (SdkPojo targetBuilder , SdkPojo sourceObject , Set <String > fieldsToIgnore ) {
142- Map <String , Object > sourceFields = retrieveSdkFields (sourceObject , sourceObject .sdkFields ());
143- List <SdkField <?>> targetSdkFields = targetBuilder .sdkFields ();
144-
145- for (SdkField <?> field : targetSdkFields ) {
146- if (fieldsToIgnore .contains (field .memberName ())) {
147- continue ;
148- }
149- field .set (targetBuilder , sourceFields .getOrDefault (field .memberName (), null ));
150- }
151- }
152-
153234 public static CreateMultipartUploadRequest toCreateMultipartUploadRequest (CopyObjectRequest copyObjectRequest ) {
154235 CreateMultipartUploadRequest .Builder builder = CreateMultipartUploadRequest .builder ();
155-
236+ validateRequestFields ( copyObjectRequest , builder . build (), COPY_OBJECT_TO_COPY_OBJECT_ALLOWED_FIELDS );
156237 setSdkFields (builder , copyObjectRequest );
157238 builder .bucket (copyObjectRequest .destinationBucket ());
158239 builder .key (copyObjectRequest .destinationKey ());
@@ -180,6 +261,7 @@ private static CopyObjectResult toCopyObjectResult(CompleteMultipartUploadRespon
180261
181262 public static AbortMultipartUploadRequest .Builder toAbortMultipartUploadRequest (CopyObjectRequest copyObjectRequest ) {
182263 AbortMultipartUploadRequest .Builder builder = AbortMultipartUploadRequest .builder ();
264+ validateRequestFields (copyObjectRequest , builder .build (), COPY_OBJECT_TO_COPY_OBJECT_ALLOWED_FIELDS );
183265 setSdkFields (builder , copyObjectRequest );
184266 builder .bucket (copyObjectRequest .destinationBucket ());
185267 builder .key (copyObjectRequest .destinationKey ());
@@ -188,6 +270,7 @@ public static AbortMultipartUploadRequest.Builder toAbortMultipartUploadRequest(
188270
189271 public static AbortMultipartUploadRequest .Builder toAbortMultipartUploadRequest (PutObjectRequest putObjectRequest ) {
190272 AbortMultipartUploadRequest .Builder builder = AbortMultipartUploadRequest .builder ();
273+ validateRequestFields (putObjectRequest , builder .build (), PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
191274 setSdkFields (builder , putObjectRequest );
192275 return builder ;
193276 }
@@ -225,4 +308,47 @@ private static Map<String, Object> retrieveSdkFields(SdkPojo sourceObject, List<
225308 field .getValueOrDefault (sourceObject )),
226309 Map ::putAll );
227310 }
311+
312+ private static void setSdkFields (SdkPojo targetBuilder , SdkPojo sourceObject ) {
313+ setSdkFields (targetBuilder , sourceObject , new HashSet <>());
314+ }
315+
316+ private static void setSdkFields (SdkPojo targetBuilder , SdkPojo sourceObject , Set <String > fieldsToIgnore ) {
317+ Map <String , Object > sourceFields = retrieveSdkFields (sourceObject , sourceObject .sdkFields ());
318+ List <SdkField <?>> targetSdkFields = targetBuilder .sdkFields ();
319+
320+ for (SdkField <?> field : targetSdkFields ) {
321+ if (fieldsToIgnore .contains (field .memberName ())) {
322+ continue ;
323+ }
324+ field .set (targetBuilder , sourceFields .getOrDefault (field .memberName (), null ));
325+ }
326+ }
327+
328+ private static void validateRequestFields (SdkPojo sourceObject , SdkPojo targetObject , Set <String > allowedFields ) {
329+ Set <String > invalidFields = new HashSet <>();
330+
331+ for (SdkField <?> sourceField : sourceObject .sdkFields ()) {
332+ String fieldName = sourceField .memberName ();
333+ Object sourceValue = sourceField .getValueOrDefault (sourceObject );
334+
335+ if (!allowedFields .contains (fieldName )) {
336+ SdkField <?> targetField = targetObject .sdkFields ()
337+ .stream ()
338+ .filter (field -> field .memberName ().equals (fieldName ))
339+ .findFirst ()
340+ .orElse (null );
341+ if (targetField != null && !Objects .equals (sourceValue , targetField .getValueOrDefault (targetObject ))) {
342+ invalidFields .add (fieldName );
343+ }
344+ }
345+ }
346+
347+ if (!invalidFields .isEmpty ()) {
348+ throw SdkClientException .create (
349+ String .format ("The following fields are not allowed: %s" ,
350+ String .join (", " , invalidFields ))
351+ );
352+ }
353+ }
228354}
0 commit comments