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