@@ -224,6 +224,14 @@ sign_v4_query_params(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Me
224224% % a fixed digest value, such as "UNSIGNED-PAYLOAD", when sending requests without
225225% % signing the body, <strong>which is expected for S3</strong>.
226226% % </dd>
227+ % % <dt>`tags'</dt>
228+ % % <dd>
229+ % % Optional tagging of the object when generating a pre-signed URL.
230+ % % The value of `tags' is a binary() in the format, for example:
231+ % % `<<"key1=value1&key2=value2">>'. The actual request to put or get the object
232+ % % must use the exact `tags' value to ensure the signature is calculated
233+ % % correctly.
234+ % % </dd>
227235% % </dl>
228236-spec sign_v4_query_params (AccessKeyID , SecretAccessKey , Region , Service , DateTime , Method , URL , Options ) -> FinalURL
229237 when AccessKeyID :: binary (),
@@ -239,7 +247,8 @@ sign_v4_query_params(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Me
239247 | {session_token , binary ()}
240248 | {ttl , non_neg_integer ()}
241249 | {body , binary ()}
242- | {body_digest , binary ()},
250+ | {body_digest , binary ()}
251+ | {tags , binary ()},
243252 FinalURL :: binary ().
244253sign_v4_query_params (AccessKeyID , SecretAccessKey , Region , Service , DateTime , Method , URL , Options )
245254 when is_binary (AccessKeyID ),
@@ -253,6 +262,7 @@ sign_v4_query_params(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Me
253262 URIEncodePath = proplists :get_value (uri_encode_path , Options , true ),
254263 TimeToLive = proplists :get_value (ttl , Options , 86400 ),
255264 SessionToken = proplists :get_value (session_token , Options , undefined ),
265+ Tags = proplists :get_value (tags , Options , undefined ),
256266 BodyDigest =
257267 case proplists :get_value (body_digest , Options , undefined ) of
258268 undefined ->
@@ -263,7 +273,13 @@ sign_v4_query_params(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Me
263273 end ,
264274 BaseParams =
265275 [{<<" X-Amz-Algorithm" >>, <<" AWS4-HMAC-SHA256" >>},
266- {<<" X-Amz-SignedHeaders" >>, <<" host" >>}],
276+ {<<" X-Amz-SignedHeaders" >>,
277+ if Tags == undefined ->
278+ <<" host" >>;
279+ Tags =/= undefined ->
280+ <<" host%3Bx-amz-tagging" >>
281+ end }
282+ ],
267283
268284 URLMap = aws_signature_utils :parse_url (URL ),
269285 LongDate = format_datetime_long (DateTime ),
@@ -276,9 +292,15 @@ sign_v4_query_params(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Me
276292
277293 FinalQueryParams = add_date_header (FinalQueryParams2 , LongDate ),
278294 HostHeader = host_header_from_url (URLMap ),
295+ Headers =
296+ if Tags == undefined ->
297+ [HostHeader ];
298+ Tags =/= undefined ->
299+ [HostHeader , {<<" X-Amz-Tagging" >>, Tags }]
300+ end ,
279301
280302 CanonicalRequest =
281- canonical_request (Method , URLMap , [ HostHeader ] , BodyDigest , URIEncodePath , FinalQueryParams ),
303+ canonical_request (Method , URLMap , Headers , BodyDigest , URIEncodePath , FinalQueryParams ),
282304
283305 HashedCanonicalRequest = aws_signature_utils :sha256_hexdigest (CanonicalRequest ),
284306 SigningKey = signing_key (SecretAccessKey , ShortDate , Region , Service ),
@@ -1055,6 +1077,36 @@ sign_v4_query_params_with_authority_well_known_port_test() ->
10551077
10561078 ? assertEqual (Expected , Actual ).
10571079
1080+ sign_v4_query_params_with_tagging_test () ->
1081+ AccessKeyID = <<" AKIAIOSFODNN7EXAMPLE" >>,
1082+ SecretAccessKey = <<" wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" >>,
1083+ Region = <<" us-east-1" >>,
1084+ Service = <<" s3" >>,
1085+ DateTime = {{2013 , 5 , 24 }, {0 , 0 , 0 }},
1086+ Method = <<" GET" >>,
1087+ URL = <<" https://examplebucket.s3.amazonaws.com/test.txt" >>,
1088+
1089+ Expected =
1090+ <<" https://examplebucket.s3.amazonaws.com/test.txt?" ,
1091+ " X-Amz-Algorithm=AWS4-HMAC-SHA256&" ,
1092+ " X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&" ,
1093+ " X-Amz-Date=20130524T000000Z&" ,
1094+ " X-Amz-Expires=86400&" ,
1095+ " X-Amz-Signature=5c14ef7998d657c3b8293b37abefaef5fa98cc775bcbfffdb2027f4ce05772ef&" ,
1096+ " X-Amz-SignedHeaders=host%3Bx-amz-tagging" >>,
1097+
1098+ Actual =
1099+ sign_v4_query_params (AccessKeyID ,
1100+ SecretAccessKey ,
1101+ Region ,
1102+ Service ,
1103+ DateTime ,
1104+ Method ,
1105+ URL ,
1106+ [{tags , <<" key1=value1&key2=value2" >>}]),
1107+
1108+ ? assertEqual (Expected , Actual ).
1109+
10581110format_date_long_test () ->
10591111 Expected = <<" 20210126T200815Z" >>,
10601112 Actual = format_datetime_long ({{2021 ,1 ,26 }, {20 ,8 ,15 }}),
0 commit comments