2424import software .amazon .awssdk .annotations .SdkInternalApi ;
2525import software .amazon .awssdk .core .SdkField ;
2626import software .amazon .awssdk .core .SdkPojo ;
27+ import software .amazon .awssdk .core .exception .SdkClientException ;
2728import software .amazon .awssdk .services .s3 .model .AbortMultipartUploadRequest ;
2829import software .amazon .awssdk .services .s3 .model .ChecksumType ;
2930import software .amazon .awssdk .services .s3 .model .CompleteMultipartUploadRequest ;
@@ -53,12 +54,104 @@ public final class SdkPojoConversionUtils {
5354 new HashSet <>(Arrays .asList ("ChecksumSHA1" , "ChecksumSHA256" , "ContentMD5" , "ChecksumCRC32C" , "ChecksumCRC32" ,
5455 "ChecksumCRC64NVME" , "ContentLength" ));
5556
57+ private static final Set <String > PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS = new HashSet <>(Arrays .asList (
58+ "ACL" ,
59+ "Bucket" ,
60+ "CacheControl" ,
61+ "ContentDisposition" ,
62+ "ContentEncoding" ,
63+ "ContentLanguage" ,
64+ "ContentLength" ,
65+ "ContentMD5" ,
66+ "ContentType" ,
67+ "ChecksumAlgorithm" ,
68+ "ChecksumCRC32" ,
69+ "ChecksumCRC32C" ,
70+ "ChecksumCRC64NVME" ,
71+ "ChecksumSHA1" ,
72+ "ChecksumSHA256" ,
73+ "Expires" ,
74+ "IfMatch" ,
75+ "IfNoneMatch" ,
76+ "GrantFullControl" ,
77+ "GrantRead" ,
78+ "GrantReadACP" ,
79+ "GrantWriteACP" ,
80+ "Key" ,
81+ "WriteOffsetBytes" ,
82+ "Metadata" ,
83+ "ServerSideEncryption" ,
84+ "StorageClass" ,
85+ "WebsiteRedirectLocation" ,
86+ "SSECustomerAlgorithm" ,
87+ "SSECustomerKey" ,
88+ "SSECustomerKeyMD5" ,
89+ "SSEKMSKeyId" ,
90+ "SSEKMSEncryptionContext" ,
91+ "BucketKeyEnabled" ,
92+ "RequestPayer" ,
93+ "Tagging" ,
94+ "ObjectLockMode" ,
95+ "ObjectLockRetainUntilDate" ,
96+ "ObjectLockLegalHoldStatus" ,
97+ "ExpectedBucketOwner"
98+ ));
99+
100+ private static final Set <String > COPY_OBJECT_TO_COPY_OBJECT_ALLOWED_FIELDS = new HashSet <>(Arrays .asList (
101+ "ACL" ,
102+ "CacheControl" ,
103+ "ChecksumAlgorithm" ,
104+ "ContentDisposition" ,
105+ "ContentEncoding" ,
106+ "ContentLanguage" ,
107+ "ContentType" ,
108+ "CopySource" ,
109+ "CopySourceIfMatch" ,
110+ "CopySourceIfModifiedSince" ,
111+ "CopySourceIfNoneMatch" ,
112+ "CopySourceIfUnmodifiedSince" ,
113+ "CopySourceSSECustomerAlgorithm" ,
114+ "CopySourceSSECustomerKey" ,
115+ "CopySourceSSECustomerKeyMD5" ,
116+ "DestinationBucket" ,
117+ "DestinationKey" ,
118+ "Expires" ,
119+ "GrantFullControl" ,
120+ "GrantRead" ,
121+ "GrantReadACP" ,
122+ "GrantWriteACP" ,
123+ "Metadata" ,
124+ "MetadataDirective" ,
125+ "TaggingDirective" ,
126+ "ServerSideEncryption" ,
127+ "SourceBucket" ,
128+ "SourceKey" ,
129+ "SourceVersionId" ,
130+ "StorageClass" ,
131+ "WebsiteRedirectLocation" ,
132+ "SSECustomerAlgorithm" ,
133+ "SSECustomerKey" ,
134+ "SSECustomerKeyMD5" ,
135+ "SSEKMSKeyId" ,
136+ "SSEKMSEncryptionContext" ,
137+ "BucketKeyEnabled" ,
138+ "RequestPayer" ,
139+ "Tagging" ,
140+ "ObjectLockMode" ,
141+ "ObjectLockRetainUntilDate" ,
142+ "ObjectLockLegalHoldStatus" ,
143+ "ExpectedBucketOwner" ,
144+ "ExpectedSourceBucketOwner"
145+ ));
146+
147+
56148 private SdkPojoConversionUtils () {
57149 }
58150
59151 public static UploadPartRequest toUploadPartRequest (PutObjectRequest putObjectRequest , int partNumber , String uploadId ) {
60152
61153 UploadPartRequest .Builder builder = UploadPartRequest .builder ();
154+ validateRequestFields (putObjectRequest , PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
62155 setSdkFields (builder , putObjectRequest , PUT_OBJECT_REQUEST_TO_UPLOAD_PART_FIELDS_TO_IGNORE );
63156 return builder .uploadId (uploadId ).partNumber (partNumber ).build ();
64157 }
@@ -67,6 +160,7 @@ public static CompleteMultipartUploadRequest toCompleteMultipartUploadRequest(Pu
67160 String uploadId , CompletedPart [] parts ,
68161 long contentLength ) {
69162 CompleteMultipartUploadRequest .Builder builder = CompleteMultipartUploadRequest .builder ();
163+ validateRequestFields (putObjectRequest , PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
70164 setSdkFields (builder , putObjectRequest );
71165
72166 builder .mpuObjectSize (contentLength );
@@ -81,6 +175,7 @@ public static CompleteMultipartUploadRequest toCompleteMultipartUploadRequest(Pu
81175 public static CreateMultipartUploadRequest toCreateMultipartUploadRequest (PutObjectRequest putObjectRequest ) {
82176
83177 CreateMultipartUploadRequest .Builder builder = CreateMultipartUploadRequest .builder ();
178+ validateRequestFields (putObjectRequest , PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
84179 setSdkFields (builder , putObjectRequest );
85180
86181 if (S3ChecksumUtils .checksumValueSpecified (putObjectRequest )) {
@@ -130,29 +225,14 @@ public static CompletedPart toCompletedPart(Part part) {
130225
131226 public static ListPartsRequest toListPartsRequest (String uploadId , PutObjectRequest putObjectRequest ) {
132227 ListPartsRequest .Builder builder = ListPartsRequest .builder ();
228+ validateRequestFields (putObjectRequest , PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
133229 setSdkFields (builder , putObjectRequest );
134230 return builder .uploadId (uploadId ).build ();
135231 }
136232
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-
153233 public static CreateMultipartUploadRequest toCreateMultipartUploadRequest (CopyObjectRequest copyObjectRequest ) {
154234 CreateMultipartUploadRequest .Builder builder = CreateMultipartUploadRequest .builder ();
155-
235+ validateRequestFields ( copyObjectRequest , COPY_OBJECT_TO_COPY_OBJECT_ALLOWED_FIELDS );
156236 setSdkFields (builder , copyObjectRequest );
157237 builder .bucket (copyObjectRequest .destinationBucket ());
158238 builder .key (copyObjectRequest .destinationKey ());
@@ -180,6 +260,7 @@ private static CopyObjectResult toCopyObjectResult(CompleteMultipartUploadRespon
180260
181261 public static AbortMultipartUploadRequest .Builder toAbortMultipartUploadRequest (CopyObjectRequest copyObjectRequest ) {
182262 AbortMultipartUploadRequest .Builder builder = AbortMultipartUploadRequest .builder ();
263+ validateRequestFields (copyObjectRequest , COPY_OBJECT_TO_COPY_OBJECT_ALLOWED_FIELDS );
183264 setSdkFields (builder , copyObjectRequest );
184265 builder .bucket (copyObjectRequest .destinationBucket ());
185266 builder .key (copyObjectRequest .destinationKey ());
@@ -188,6 +269,7 @@ public static AbortMultipartUploadRequest.Builder toAbortMultipartUploadRequest(
188269
189270 public static AbortMultipartUploadRequest .Builder toAbortMultipartUploadRequest (PutObjectRequest putObjectRequest ) {
190271 AbortMultipartUploadRequest .Builder builder = AbortMultipartUploadRequest .builder ();
272+ validateRequestFields (putObjectRequest , PUT_OBJECT_TO_UPLOAD_PART_ALLOWED_FIELDS );
191273 setSdkFields (builder , putObjectRequest );
192274 return builder ;
193275 }
@@ -225,4 +307,40 @@ private static Map<String, Object> retrieveSdkFields(SdkPojo sourceObject, List<
225307 field .getValueOrDefault (sourceObject )),
226308 Map ::putAll );
227309 }
310+
311+ private static void setSdkFields (SdkPojo targetBuilder , SdkPojo sourceObject ) {
312+ setSdkFields (targetBuilder , sourceObject , new HashSet <>());
313+ }
314+
315+ private static void setSdkFields (SdkPojo targetBuilder , SdkPojo sourceObject , Set <String > fieldsToIgnore ) {
316+ Map <String , Object > sourceFields = retrieveSdkFields (sourceObject , sourceObject .sdkFields ());
317+ List <SdkField <?>> targetSdkFields = targetBuilder .sdkFields ();
318+
319+ for (SdkField <?> field : targetSdkFields ) {
320+ if (fieldsToIgnore .contains (field .memberName ())) {
321+ continue ;
322+ }
323+ field .set (targetBuilder , sourceFields .getOrDefault (field .memberName (), null ));
324+ }
325+ }
326+
327+ private static void validateRequestFields (SdkPojo sourceObject , Set <String > allowedFields ) {
328+ Set <String > invalidFields = new HashSet <>();
329+
330+ for (SdkField <?> sourceField : sourceObject .sdkFields ()) {
331+ String fieldName = sourceField .memberName ();
332+ Object rawValue = sourceField .getValueOrDefault (sourceObject );
333+
334+ if (rawValue != null && !allowedFields .contains (fieldName )) {
335+ invalidFields .add (fieldName );
336+ }
337+ }
338+
339+ if (!invalidFields .isEmpty ()) {
340+ throw SdkClientException .create (
341+ String .format ("The following fields are not allowed: %s" ,
342+ String .join (", " , invalidFields ))
343+ );
344+ }
345+ }
228346}
0 commit comments