aws-sdk-s3 1.21.0 → 1.117.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +930 -0
  3. data/LICENSE.txt +202 -0
  4. data/VERSION +1 -0
  5. data/lib/aws-sdk-s3/bucket.rb +393 -75
  6. data/lib/aws-sdk-s3/bucket_acl.rb +57 -14
  7. data/lib/aws-sdk-s3/bucket_cors.rb +67 -13
  8. data/lib/aws-sdk-s3/bucket_lifecycle.rb +54 -15
  9. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +56 -15
  10. data/lib/aws-sdk-s3/bucket_logging.rb +52 -15
  11. data/lib/aws-sdk-s3/bucket_notification.rb +47 -17
  12. data/lib/aws-sdk-s3/bucket_policy.rb +51 -13
  13. data/lib/aws-sdk-s3/bucket_region_cache.rb +2 -0
  14. data/lib/aws-sdk-s3/bucket_request_payment.rb +51 -12
  15. data/lib/aws-sdk-s3/bucket_tagging.rb +59 -13
  16. data/lib/aws-sdk-s3/bucket_versioning.rb +118 -12
  17. data/lib/aws-sdk-s3/bucket_website.rb +66 -13
  18. data/lib/aws-sdk-s3/client.rb +11422 -2518
  19. data/lib/aws-sdk-s3/client_api.rb +1196 -155
  20. data/lib/aws-sdk-s3/customizations/bucket.rb +53 -36
  21. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
  22. data/lib/aws-sdk-s3/customizations/object.rb +200 -62
  23. data/lib/aws-sdk-s3/customizations/object_summary.rb +5 -0
  24. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
  25. data/lib/aws-sdk-s3/customizations.rb +4 -1
  26. data/lib/aws-sdk-s3/encryption/client.rb +23 -6
  27. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
  28. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
  29. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
  30. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
  31. data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
  32. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +11 -3
  33. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
  34. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
  35. data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
  36. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +34 -3
  37. data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
  38. data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
  39. data/lib/aws-sdk-s3/encryption.rb +4 -0
  40. data/lib/aws-sdk-s3/encryptionV2/client.rb +566 -0
  41. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +222 -0
  42. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
  43. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
  44. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +65 -0
  45. data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
  46. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
  47. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
  48. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
  49. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
  50. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +169 -0
  51. data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
  52. data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
  53. data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
  54. data/lib/aws-sdk-s3/endpoint_parameters.rb +142 -0
  55. data/lib/aws-sdk-s3/endpoint_provider.rb +2020 -0
  56. data/lib/aws-sdk-s3/endpoints.rb +2149 -0
  57. data/lib/aws-sdk-s3/errors.rb +123 -1
  58. data/lib/aws-sdk-s3/event_streams.rb +20 -7
  59. data/lib/aws-sdk-s3/file_downloader.rb +17 -10
  60. data/lib/aws-sdk-s3/file_part.rb +11 -6
  61. data/lib/aws-sdk-s3/file_uploader.rb +33 -14
  62. data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
  63. data/lib/aws-sdk-s3/multipart_file_uploader.rb +78 -19
  64. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +54 -15
  65. data/lib/aws-sdk-s3/multipart_upload.rb +178 -28
  66. data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
  67. data/lib/aws-sdk-s3/multipart_upload_part.rb +237 -44
  68. data/lib/aws-sdk-s3/object.rb +897 -154
  69. data/lib/aws-sdk-s3/object_acl.rb +81 -20
  70. data/lib/aws-sdk-s3/object_copier.rb +2 -0
  71. data/lib/aws-sdk-s3/object_multipart_copier.rb +2 -0
  72. data/lib/aws-sdk-s3/object_summary.rb +649 -139
  73. data/lib/aws-sdk-s3/object_version.rb +167 -65
  74. data/lib/aws-sdk-s3/plugins/accelerate.rb +17 -64
  75. data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
  76. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +7 -43
  77. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +20 -3
  78. data/lib/aws-sdk-s3/plugins/dualstack.rb +7 -50
  79. data/lib/aws-sdk-s3/plugins/endpoints.rb +262 -0
  80. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +5 -4
  81. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
  82. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +11 -3
  83. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +44 -0
  84. data/lib/aws-sdk-s3/plugins/location_constraint.rb +2 -0
  85. data/lib/aws-sdk-s3/plugins/md5s.rb +34 -27
  86. data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
  87. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
  88. data/lib/aws-sdk-s3/plugins/s3_signer.rb +55 -92
  89. data/lib/aws-sdk-s3/plugins/skip_whole_multipart_get_checksums.rb +31 -0
  90. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
  91. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +139 -0
  92. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
  93. data/lib/aws-sdk-s3/presigned_post.rb +108 -56
  94. data/lib/aws-sdk-s3/presigner.rb +169 -77
  95. data/lib/aws-sdk-s3/resource.rb +45 -5
  96. data/lib/aws-sdk-s3/types.rb +8564 -3891
  97. data/lib/aws-sdk-s3/waiters.rb +67 -1
  98. data/lib/aws-sdk-s3.rb +16 -6
  99. metadata +37 -13
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
  require 'base64'
3
5
 
@@ -96,7 +98,7 @@ module Aws
96
98
  # or call the associated method.
97
99
  #
98
100
  # ```ruby
99
- # post = Aws::S3::PresignedPost.new(creds, region, bucket).
101
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket)
100
102
  # post.content_type('text/plain')
101
103
  # ```
102
104
  #
@@ -174,44 +176,76 @@ module Aws
174
176
  # ```
175
177
  #
176
178
  class PresignedPost
179
+ @@allowed_fields = []
177
180
 
178
181
  # @param [Credentials] credentials Security credentials for signing
179
182
  # the post policy.
180
183
  # @param [String] bucket_region Region of the target bucket.
181
184
  # @param [String] bucket_name Name of the target bucket.
185
+ # @option options [Boolean] :use_accelerate_endpoint (false) When `true`,
186
+ # PresignedPost will attempt to use accelerated endpoint.
187
+ # @option options [String] :url See {PresignedPost#url}.
188
+ # @option options [Sting, Array<String>] :allow_any
189
+ # See {PresignedPost#allow_any}.
182
190
  # @option options [Time] :signature_expiration Specify when the signature on
183
191
  # the post will expire. Defaults to one hour from creation of the
184
192
  # presigned post. May not exceed one week from creation time.
185
193
  # @option options [String] :key See {PresignedPost#key}.
186
- # @option options [String] :key_starts_with See {PresignedPost#key_starts_with}.
194
+ # @option options [String] :key_starts_with
195
+ # See {PresignedPost#key_starts_with}.
187
196
  # @option options [String] :acl See {PresignedPost#acl}.
188
- # @option options [String] :acl_starts_with See {PresignedPost#acl_starts_with}.
189
- # @option options [String] :cache_control See {PresignedPost#cache_control}.
190
- # @option options [String] :cache_control_starts_with See {PresignedPost#cache_control_starts_with}.
197
+ # @option options [String] :acl_starts_with
198
+ # See {PresignedPost#acl_starts_with}.
199
+ # @option options [String] :cache_control
200
+ # See {PresignedPost#cache_control}.
201
+ # @option options [String] :cache_control_starts_with
202
+ # See {PresignedPost#cache_control_starts_with}.
191
203
  # @option options [String] :content_type See {PresignedPost#content_type}.
192
- # @option options [String] :content_type_starts_with See {PresignedPost#content_type_starts_with}.
193
- # @option options [String] :content_disposition See {PresignedPost#content_disposition}.
194
- # @option options [String] :content_disposition_starts_with See {PresignedPost#content_disposition_starts_with}.
195
- # @option options [String] :content_encoding See {PresignedPost#content_encoding}.
196
- # @option options [String] :content_encoding_starts_with See {PresignedPost#content_encoding_starts_with}.
197
- # @option options [String] :expires See {PresignedPost#expires}.
198
- # @option options [String] :expires_starts_with See {PresignedPost#expires_starts_with}.
199
- # @option options [Range<Integer>] :content_length_range See {PresignedPost#content_length_range}.
200
- # @option options [String] :success_action_redirect See {PresignedPost#success_action_redirect}.
201
- # @option options [String] :success_action_redirect_starts_with See {PresignedPost#success_action_redirect_starts_with}.
202
- # @option options [String] :success_action_status See {PresignedPost#success_action_status}.
203
- # @option options [String] :storage_class See {PresignedPost#storage_class}.
204
- # @option options [String] :website_redirect_location See {PresignedPost#website_redirect_location}.
205
- # @option options [Hash<String,String>] :metadata See {PresignedPost#metadata}.
206
- # @option options [Hash<String,String>] :metadata_starts_with See {PresignedPost#metadata_starts_with}.
207
- # @option options [String] :server_side_encryption See {PresignedPost#server_side_encryption}.
208
- # @option options [String] :server_side_encryption_aws_kms_key_id See {PresignedPost#server_side_encryption_aws_kms_key_id}.
209
- # @option options [String] :server_side_encryption_customer_algorithm See {PresignedPost#server_side_encryption_customer_algorithm}.
210
- # @option options [String] :server_side_encryption_customer_key See {PresignedPost#server_side_encryption_customer_key}.
204
+ # @option options [String] :content_type_starts_with
205
+ # See {PresignedPost#content_type_starts_with}.
206
+ # @option options [String] :content_disposition
207
+ # See {PresignedPost#content_disposition}.
208
+ # @option options [String] :content_disposition_starts_with
209
+ # See {PresignedPost#content_disposition_starts_with}.
210
+ # @option options [String] :content_encoding
211
+ # See {PresignedPost#content_encoding}.
212
+ # @option options [String] :content_encoding_starts_with
213
+ # See {PresignedPost#content_encoding_starts_with}.
214
+ # @option options [Time] :expires See {PresignedPost#expires}.
215
+ # @option options [String] :expires_starts_with
216
+ # See {PresignedPost#expires_starts_with}.
217
+ # @option options [Range<Integer>] :content_length_range
218
+ # See {PresignedPost#content_length_range}.
219
+ # @option options [String] :success_action_redirect
220
+ # See {PresignedPost#success_action_redirect}.
221
+ # @option options [String] :success_action_redirect_starts_with
222
+ # See {PresignedPost#success_action_redirect_starts_with}.
223
+ # @option options [String] :success_action_status
224
+ # See {PresignedPost#success_action_status}.
225
+ # @option options [String] :storage_class
226
+ # See {PresignedPost#storage_class}.
227
+ # @option options [String] :website_redirect_location
228
+ # See {PresignedPost#website_redirect_location}.
229
+ # @option options [Hash<String,String>] :metadata
230
+ # See {PresignedPost#metadata}.
231
+ # @option options [Hash<String,String>] :metadata_starts_with
232
+ # See {PresignedPost#metadata_starts_with}.
233
+ # @option options [String] :server_side_encryption
234
+ # See {PresignedPost#server_side_encryption}.
235
+ # @option options [String] :server_side_encryption_aws_kms_key_id
236
+ # See {PresignedPost#server_side_encryption_aws_kms_key_id}.
237
+ # @option options [String] :server_side_encryption_customer_algorithm
238
+ # See {PresignedPost#server_side_encryption_customer_algorithm}.
239
+ # @option options [String] :server_side_encryption_customer_key
240
+ # See {PresignedPost#server_side_encryption_customer_key}.
241
+ # @option options [String] :server_side_encryption_customer_key_starts_with
242
+ # See {PresignedPost#server_side_encryption_customer_key_starts_with}.
211
243
  def initialize(credentials, bucket_region, bucket_name, options = {})
212
244
  @credentials = credentials.credentials
213
245
  @bucket_region = bucket_region
214
246
  @bucket_name = bucket_name
247
+ @accelerate = !!options.delete(:use_accelerate_endpoint)
248
+ options.delete(:url) if @accelerate # resource methods pass url
215
249
  @url = options.delete(:url) || bucket_url
216
250
  @fields = {}
217
251
  @key_set = false
@@ -221,7 +255,12 @@ module Aws
221
255
  case option_name
222
256
  when :allow_any then allow_any(option_value)
223
257
  when :signature_expiration then @signature_expiration = option_value
224
- else send("#{option_name}", option_value)
258
+ else
259
+ if @@allowed_fields.include?(option_name)
260
+ send("#{option_name}", option_value)
261
+ else
262
+ raise ArgumentError, "Unsupported option: #{option_name}"
263
+ end
225
264
  end
226
265
  end
227
266
  end
@@ -234,7 +273,7 @@ module Aws
234
273
  # as hidden input fields.
235
274
  def fields
236
275
  check_required_values!
237
- datetime = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
276
+ datetime = Time.now.utc.strftime('%Y%m%dT%H%M%SZ')
238
277
  fields = @fields.dup
239
278
  fields.update('policy' => policy(datetime))
240
279
  fields.update(signature_fields(datetime))
@@ -253,24 +292,30 @@ module Aws
253
292
  end
254
293
 
255
294
  # @api private
256
- def self.define_field(field, *args)
295
+ def self.define_field(field, *args, &block)
296
+ @@allowed_fields << field
257
297
  options = args.last.is_a?(Hash) ? args.pop : {}
258
298
  field_name = args.last || field.to_s
259
299
 
260
- define_method("#{field}") do |value|
261
- with(field_name, value)
262
- end
300
+ if block_given?
301
+ define_method("#{field}", block)
302
+ else
303
+ define_method("#{field}") do |value|
304
+ with(field_name, value)
305
+ end
263
306
 
264
- if options[:starts_with]
265
- define_method("#{field}_starts_with") do |value|
266
- starts_with(field_name, value)
307
+ if options[:starts_with]
308
+ @@allowed_fields << "#{field}_starts_with".to_sym
309
+ define_method("#{field}_starts_with") do |value|
310
+ starts_with(field_name, value)
311
+ end
267
312
  end
268
313
  end
269
314
  end
270
315
 
271
316
  # @!group Fields
272
317
 
273
- # The key to use for the uploaded object. Use can use `${filename}`
318
+ # The key to use for the uploaded object. You can use `${filename}`
274
319
  # as a variable in the key. This will be replaced with the name
275
320
  # of the file as provided by the user.
276
321
  #
@@ -281,7 +326,7 @@ module Aws
281
326
  # @param [String] key
282
327
  # @see http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html)
283
328
  # @return [self]
284
- def key(key)
329
+ define_field(:key) do |key|
285
330
  @key_set = true
286
331
  with('key', key)
287
332
  end
@@ -290,7 +335,7 @@ module Aws
290
335
  # @param [String] prefix
291
336
  # @see #key
292
337
  # @return [self]
293
- def key_starts_with(prefix)
338
+ define_field(:key_starts_with) do |prefix|
294
339
  @key_set = true
295
340
  starts_with('key', prefix)
296
341
  end
@@ -373,21 +418,21 @@ module Aws
373
418
  # @param [Time] time
374
419
  # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21
375
420
  # @return [self]
376
- def expires(time)
421
+ define_field(:expires) do |time|
377
422
  with('Expires', time.httpdate)
378
423
  end
379
424
 
380
425
  # @param [String] prefix
381
426
  # @see #expires
382
427
  # @return [self]
383
- def expires_starts_with(prefix)
428
+ define_field(:expires_starts_with) do |prefix|
384
429
  starts_with('Expires', prefix)
385
430
  end
386
431
 
387
432
  # The minimum and maximum allowable size for the uploaded content.
388
433
  # @param [Range<Integer>] byte_range
389
434
  # @return [self]
390
- def content_length_range(byte_range)
435
+ define_field(:content_length_range) do |byte_range|
391
436
  min = byte_range.begin
392
437
  max = byte_range.end
393
438
  max -= 1 if byte_range.exclude_end?
@@ -466,7 +511,7 @@ module Aws
466
511
  # prefixed with "x-amz-meta-".
467
512
  # @param [Hash<String,String>] hash
468
513
  # @return [self]
469
- def metadata(hash)
514
+ define_field(:metadata) do |hash|
470
515
  hash.each do |key, value|
471
516
  with("x-amz-meta-#{key}", value)
472
517
  end
@@ -477,7 +522,7 @@ module Aws
477
522
  # @param [Hash<String,String>] hash
478
523
  # @see #metadata
479
524
  # @return [self]
480
- def metadata_starts_with(hash)
525
+ define_field(:metadata_starts_with) do |hash|
481
526
  hash.each do |key, value|
482
527
  starts_with("x-amz-meta-#{key}", value)
483
528
  end
@@ -505,7 +550,10 @@ module Aws
505
550
  # (KMS) master encryption key to use for the object.
506
551
  # @param [String] value
507
552
  # @return [self]
508
- define_field(:server_side_encryption_aws_kms_key_id, 'x-amz-server-side-encryption-aws-kms-key-id')
553
+ define_field(
554
+ :server_side_encryption_aws_kms_key_id,
555
+ 'x-amz-server-side-encryption-aws-kms-key-id'
556
+ )
509
557
 
510
558
  # @!endgroup
511
559
 
@@ -518,7 +566,10 @@ module Aws
518
566
  # @param [String] value
519
567
  # @see #server_side_encryption_customer_key
520
568
  # @return [self]
521
- define_field(:server_side_encryption_customer_algorithm, 'x-amz-server-side-encryption-customer-algorithm')
569
+ define_field(
570
+ :server_side_encryption_customer_algorithm,
571
+ 'x-amz-server-side-encryption-customer-algorithm'
572
+ )
522
573
 
523
574
  # Specifies the customer-provided encryption key for Amazon S3 to use
524
575
  # in encrypting data. This value is used to store the object and then
@@ -529,7 +580,7 @@ module Aws
529
580
  # @param [String] value
530
581
  # @see #server_side_encryption_customer_algorithm
531
582
  # @return [self]
532
- def server_side_encryption_customer_key(value)
583
+ define_field(:server_side_encryption_customer_key) do |value|
533
584
  field_name = 'x-amz-server-side-encryption-customer-key'
534
585
  with(field_name, base64(value))
535
586
  with(field_name + '-MD5', base64(OpenSSL::Digest::MD5.digest(value)))
@@ -538,7 +589,7 @@ module Aws
538
589
  # @param [String] prefix
539
590
  # @see #server_side_encryption_customer_key
540
591
  # @return [self]
541
- def server_side_encryption_customer_key_starts_with(prefix)
592
+ define_field(:server_side_encryption_customer_key_starts_with) do |prefix|
542
593
  field_name = 'x-amz-server-side-encryption-customer-key'
543
594
  starts_with(field_name, prefix)
544
595
  end
@@ -571,21 +622,22 @@ module Aws
571
622
 
572
623
  def check_required_values!
573
624
  unless @key_set
574
- msg = "key required; you must provide a key via :key, "
575
- msg << ":key_starts_with, or :allow_any => ['key']"
625
+ msg = 'key required; you must provide a key via :key, '\
626
+ ":key_starts_with, or :allow_any => ['key']"
576
627
  raise msg
577
628
  end
578
629
  end
579
630
 
580
631
  def bucket_url
581
- url = Aws::Partitions::EndpointProvider.resolve(@bucket_region, 's3')
582
- url = URI.parse(url)
583
- if Plugins::BucketDns.dns_compatible?(@bucket_name, true)
584
- url.host = @bucket_name + '.' + url.host
585
- else
586
- url.path = '/' + @bucket_name
587
- end
588
- url.to_s
632
+ # Taken from Aws::S3::Endpoints module
633
+ params = Aws::S3::EndpointParameters.new(
634
+ bucket: @bucket_name,
635
+ region: @bucket_region,
636
+ accelerate: @accelerate,
637
+ use_global_endpoint: true
638
+ )
639
+ endpoint = Aws::S3::EndpointProvider.new.resolve_endpoint(params)
640
+ endpoint.url
589
641
  end
590
642
 
591
643
  # @return [Hash]
@@ -613,7 +665,7 @@ module Aws
613
665
 
614
666
  def signature(datetime, string_to_sign)
615
667
  k_secret = @credentials.secret_access_key
616
- k_date = hmac("AWS4" + k_secret, datetime[0,8])
668
+ k_date = hmac('AWS4' + k_secret, datetime[0,8])
617
669
  k_region = hmac(k_date, @bucket_region)
618
670
  k_service = hmac(k_region, 's3')
619
671
  k_credentials = hmac(k_service, 'aws4_request')
@@ -1,27 +1,94 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
-
4
- # Allows you to create presigned URLs for S3 operations.
5
- #
6
- # Example Use:
7
- #
8
- # signer = Aws::S3::Presigner.new
9
- # url = signer.presigned_url(:get_object, bucket: "bucket", key: "key")
10
- #
11
5
  class Presigner
12
-
13
6
  # @api private
14
7
  ONE_WEEK = 60 * 60 * 24 * 7
15
8
 
16
9
  # @api private
17
10
  FIFTEEN_MINUTES = 60 * 15
18
11
 
12
+ # @api private
13
+ BLACKLISTED_HEADERS = [
14
+ 'accept',
15
+ 'amz-sdk-request',
16
+ 'cache-control',
17
+ 'content-length', # due to a ELB bug
18
+ 'expect',
19
+ 'from',
20
+ 'if-match',
21
+ 'if-none-match',
22
+ 'if-modified-since',
23
+ 'if-unmodified-since',
24
+ 'if-range',
25
+ 'max-forwards',
26
+ 'pragma',
27
+ 'proxy-authorization',
28
+ 'referer',
29
+ 'te',
30
+ 'user-agent'
31
+ ].freeze
32
+
19
33
  # @option options [Client] :client Optionally provide an existing
20
34
  # S3 client
21
35
  def initialize(options = {})
22
36
  @client = options[:client] || Aws::S3::Client.new
23
37
  end
24
38
 
39
+ # Create presigned URLs for S3 operations.
40
+ #
41
+ # @example
42
+ # signer = Aws::S3::Presigner.new
43
+ # url = signer.presigned_url(:get_object, bucket: "bucket", key: "key")
44
+ #
45
+ # @param [Symbol] method Symbolized method name of the operation you want
46
+ # to presign.
47
+ #
48
+ # @option params [Integer] :expires_in (900) The number of seconds
49
+ # before the presigned URL expires. Defaults to 15 minutes. As signature
50
+ # version 4 has a maximum expiry time of one week for presigned URLs,
51
+ # attempts to set this value to greater than one week (604800) will
52
+ # raise an exception.
53
+ #
54
+ # @option params [Time] :time (Time.now) The starting time for when the
55
+ # presigned url becomes active.
56
+ #
57
+ # @option params [Boolean] :secure (true) When `false`, a HTTP URL
58
+ # is returned instead of the default HTTPS URL.
59
+ #
60
+ # @option params [Boolean] :virtual_host (false) When `true`, the
61
+ # bucket name will be used as the hostname.
62
+ #
63
+ # @option params [Boolean] :use_accelerate_endpoint (false) When `true`,
64
+ # Presigner will attempt to use accelerated endpoint.
65
+ #
66
+ # @option params [Array<String>] :whitelist_headers ([]) Additional
67
+ # headers to be included for the signed request. Certain headers beyond
68
+ # the authorization header could, in theory, be changed for various
69
+ # reasons (including but not limited to proxies) while in transit and
70
+ # after signing. This would lead to signature errors being returned,
71
+ # despite no actual problems with signing. (see BLACKLISTED_HEADERS)
72
+ #
73
+ # @raise [ArgumentError] Raises an ArgumentError if `:expires_in`
74
+ # exceeds one week.
75
+ #
76
+ # @return [String] a presigned url
77
+ def presigned_url(method, params = {})
78
+ url, _headers = _presigned_request(method, params)
79
+ url
80
+ end
81
+
82
+ # Allows you to create presigned URL requests for S3 operations. This
83
+ # method returns a tuple containing the URL and the signed X-amz-* headers
84
+ # to be used with the presigned url.
85
+ #
86
+ # @example
87
+ # signer = Aws::S3::Presigner.new
88
+ # url, headers = signer.presigned_request(
89
+ # :get_object, bucket: "bucket", key: "key"
90
+ # )
91
+ #
25
92
  # @param [Symbol] method Symbolized method name of the operation you want
26
93
  # to presign.
27
94
  #
@@ -31,6 +98,9 @@ module Aws
31
98
  # attempts to set this value to greater than one week (604800) will
32
99
  # raise an exception.
33
100
  #
101
+ # @option params [Time] :time (Time.now) The starting time for when the
102
+ # presigned url becomes active.
103
+ #
34
104
  # @option params [Boolean] :secure (true) When `false`, a HTTP URL
35
105
  # is returned instead of the default HTTPS URL.
36
106
  #
@@ -38,37 +108,57 @@ module Aws
38
108
  # bucket name will be used as the hostname. This will cause
39
109
  # the returned URL to be 'http' and not 'https'.
40
110
  #
111
+ # @option params [Boolean] :use_accelerate_endpoint (false) When `true`,
112
+ # Presigner will attempt to use accelerated endpoint.
113
+ #
114
+ # @option params [Array<String>] :whitelist_headers ([]) Additional
115
+ # headers to be included for the signed request. Certain headers beyond
116
+ # the authorization header could, in theory, be changed for various
117
+ # reasons (including but not limited to proxies) while in transit and
118
+ # after signing. This would lead to signature errors being returned,
119
+ # despite no actual problems with signing. (see BLACKLISTED_HEADERS)
120
+ #
41
121
  # @raise [ArgumentError] Raises an ArgumentError if `:expires_in`
42
122
  # exceeds one week.
43
123
  #
44
- def presigned_url(method, params = {})
45
- if params[:key].nil? or params[:key] == ''
46
- raise ArgumentError, ":key must not be blank"
47
- end
48
- virtual_host = !!params.delete(:virtual_host)
49
- scheme = http_scheme(params, virtual_host)
124
+ # @return [String, Hash] A tuple with a presigned URL and headers that
125
+ # should be included with the request.
126
+ def presigned_request(method, params = {})
127
+ _presigned_request(method, params, false)
128
+ end
129
+
130
+ private
131
+
132
+ def _presigned_request(method, params, hoist = true)
133
+ virtual_host = params.delete(:virtual_host)
134
+ time = params.delete(:time)
135
+ unsigned_headers = unsigned_headers(params)
136
+ secure = params.delete(:secure) != false
137
+ expires_in = expires_in(params)
50
138
 
51
139
  req = @client.build_request(method, params)
52
140
  use_bucket_as_hostname(req) if virtual_host
53
- sign_but_dont_send(req, expires_in(params), scheme)
54
- req.send_request.data
55
- end
141
+ handle_presigned_url_context(req)
56
142
 
57
- private
143
+ x_amz_headers = sign_but_dont_send(
144
+ req, expires_in, secure, time, unsigned_headers, hoist
145
+ )
146
+ [req.send_request.data, x_amz_headers]
147
+ end
58
148
 
59
- def http_scheme(params, virtual_host)
60
- if params.delete(:secure) == false || virtual_host
61
- 'http'
62
- else
63
- @client.config.endpoint.scheme
64
- end
149
+ def unsigned_headers(params)
150
+ whitelist_headers = params.delete(:whitelist_headers) || []
151
+ BLACKLISTED_HEADERS - whitelist_headers
65
152
  end
66
153
 
67
154
  def expires_in(params)
68
- if expires_in = params.delete(:expires_in)
155
+ if (expires_in = params.delete(:expires_in))
69
156
  if expires_in > ONE_WEEK
70
- msg = "expires_in value of #{expires_in} exceeds one-week maximum"
71
- raise ArgumentError, msg
157
+ raise ArgumentError,
158
+ "expires_in value of #{expires_in} exceeds one-week maximum."
159
+ elsif expires_in <= 0
160
+ raise ArgumentError,
161
+ "expires_in value of #{expires_in} cannot be 0 or less."
72
162
  end
73
163
  expires_in
74
164
  else
@@ -77,89 +167,91 @@ module Aws
77
167
  end
78
168
 
79
169
  def use_bucket_as_hostname(req)
80
- req.handlers.remove(Plugins::BucketDns::Handler)
81
- req.handle do |context|
170
+ req.handle(priority: 35) do |context|
82
171
  uri = context.http_request.endpoint
83
172
  uri.host = context.params[:bucket]
84
173
  uri.path.sub!("/#{context.params[:bucket]}", '')
85
- uri.scheme = 'http'
86
- uri.port = 80
174
+ @handler.call(context)
175
+ end
176
+ end
177
+
178
+ # Used for excluding presigned_urls from API request count.
179
+ #
180
+ # Store context information as early as possible, to allow
181
+ # handlers to perform decisions based on this flag if need.
182
+ def handle_presigned_url_context(req)
183
+ req.handle(step: :initialize, priority: 98) do |context|
184
+ context[:presigned_url] = true
87
185
  @handler.call(context)
88
186
  end
89
187
  end
90
188
 
91
189
  # @param [Seahorse::Client::Request] req
92
- def sign_but_dont_send(req, expires_in, scheme)
190
+ def sign_but_dont_send(
191
+ req, expires_in, secure, time, unsigned_headers, hoist = true
192
+ )
193
+ x_amz_headers = {}
93
194
 
94
195
  http_req = req.context.http_request
95
196
 
96
197
  req.handlers.remove(Aws::S3::Plugins::S3Signer::LegacyHandler)
97
- req.handlers.remove(Aws::S3::Plugins::S3Signer::V4Handler)
198
+ req.handlers.remove(Aws::Plugins::Sign::Handler)
98
199
  req.handlers.remove(Seahorse::Client::Plugins::ContentLength::Handler)
99
200
 
100
- signer = build_signer(req.context.config)
101
- req.context[:presigned_url] = true
102
-
103
201
  req.handle(step: :send) do |context|
104
-
105
- if scheme != http_req.endpoint.scheme
106
- endpoint = http_req.endpoint.dup
107
- endpoint.scheme = scheme
108
- endpoint.port = (scheme == 'http' ? 80 : 443)
109
- http_req.endpoint = URI.parse(endpoint.to_s)
202
+ # if an endpoint was not provided, force secure or insecure
203
+ if context.config.regional_endpoint
204
+ http_req.endpoint.scheme = secure ? 'https' : 'http'
205
+ http_req.endpoint.port = secure ? 443 : 80
110
206
  end
111
207
 
112
- # hoist x-amz-* headers to the querystring
113
208
  query = http_req.endpoint.query ? http_req.endpoint.query.split('&') : []
114
- http_req.headers.keys.each do |key|
115
- if key.match(/^x-amz/i)
116
- value = Aws::Sigv4::Signer.uri_escape(http_req.headers.delete(key))
209
+ http_req.headers.each do |key, value|
210
+ next unless key =~ /^x-amz/i
211
+
212
+ if hoist
213
+ value = Aws::Sigv4::Signer.uri_escape(value)
117
214
  key = Aws::Sigv4::Signer.uri_escape(key)
215
+ # hoist x-amz-* headers to the querystring
216
+ http_req.headers.delete(key)
118
217
  query << "#{key}=#{value}"
218
+ else
219
+ x_amz_headers[key] = value
119
220
  end
120
221
  end
121
222
  http_req.endpoint.query = query.join('&') unless query.empty?
122
223
 
224
+ auth_scheme = context[:auth_scheme]
225
+ scheme_name = auth_scheme['name']
226
+ region = if scheme_name == 'sigv4a'
227
+ auth_scheme['signingRegionSet'].first
228
+ else
229
+ auth_scheme['signingRegion']
230
+ end
231
+ signer = Aws::Sigv4::Signer.new(
232
+ service: auth_scheme['signingName'] || 's3',
233
+ region: region || context.config.region,
234
+ credentials_provider: context.config.credentials,
235
+ signing_algorithm: scheme_name.to_sym,
236
+ uri_escape_path: !!!auth_scheme['disableDoubleEncoding'],
237
+ unsigned_headers: unsigned_headers,
238
+ apply_checksum_header: false
239
+ )
240
+
123
241
  url = signer.presign_url(
124
242
  http_method: http_req.http_method,
125
243
  url: http_req.endpoint,
126
244
  headers: http_req.headers,
127
245
  body_digest: 'UNSIGNED-PAYLOAD',
128
- expires_in: expires_in
246
+ expires_in: expires_in,
247
+ time: time
129
248
  ).to_s
130
249
 
131
250
  Seahorse::Client::Response.new(context: context, data: url)
132
251
  end
252
+ # Return the headers
253
+ x_amz_headers
133
254
  end
134
-
135
- def build_signer(cfg)
136
- Aws::Sigv4::Signer.new(
137
- service: 's3',
138
- region: cfg.region,
139
- credentials_provider: cfg.credentials,
140
- unsigned_headers: [
141
- 'cache-control',
142
- 'content-length', # due to a ELB bug
143
- 'expect',
144
- 'max-forwards',
145
- 'pragma',
146
- 'te',
147
- 'if-match',
148
- 'if-none-match',
149
- 'if-modified-since',
150
- 'if-unmodified-since',
151
- 'if-range',
152
- 'accept',
153
- 'proxy-authorization',
154
- 'from',
155
- 'referer',
156
- 'user-agent',
157
- 'x-amzn-trace-id'
158
- ],
159
- uri_escape_path: false
160
- )
161
- end
162
-
163
255
  end
164
256
  end
165
257
  end