aws-sdk-s3 1.48.0 → 1.169.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1270 -0
  3. data/LICENSE.txt +202 -0
  4. data/VERSION +1 -0
  5. data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
  6. data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
  7. data/lib/aws-sdk-s3/bucket.rb +959 -106
  8. data/lib/aws-sdk-s3/bucket_acl.rb +64 -18
  9. data/lib/aws-sdk-s3/bucket_cors.rb +79 -18
  10. data/lib/aws-sdk-s3/bucket_lifecycle.rb +66 -20
  11. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +106 -21
  12. data/lib/aws-sdk-s3/bucket_logging.rb +68 -16
  13. data/lib/aws-sdk-s3/bucket_notification.rb +52 -20
  14. data/lib/aws-sdk-s3/bucket_policy.rb +107 -17
  15. data/lib/aws-sdk-s3/bucket_region_cache.rb +11 -5
  16. data/lib/aws-sdk-s3/bucket_request_payment.rb +60 -15
  17. data/lib/aws-sdk-s3/bucket_tagging.rb +71 -18
  18. data/lib/aws-sdk-s3/bucket_versioning.rb +133 -17
  19. data/lib/aws-sdk-s3/bucket_website.rb +78 -21
  20. data/lib/aws-sdk-s3/client.rb +13765 -1019
  21. data/lib/aws-sdk-s3/client_api.rb +1137 -197
  22. data/lib/aws-sdk-s3/customizations/bucket.rb +56 -37
  23. data/lib/aws-sdk-s3/customizations/errors.rb +40 -0
  24. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
  25. data/lib/aws-sdk-s3/customizations/object.rb +288 -68
  26. data/lib/aws-sdk-s3/customizations/object_summary.rb +10 -0
  27. data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
  28. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
  29. data/lib/aws-sdk-s3/customizations/types/permanent_redirect.rb +26 -0
  30. data/lib/aws-sdk-s3/customizations.rb +27 -28
  31. data/lib/aws-sdk-s3/encryption/client.rb +28 -7
  32. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
  33. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
  34. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
  35. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
  36. data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
  37. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +2 -0
  38. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
  39. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
  40. data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
  41. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +46 -11
  42. data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
  43. data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
  44. data/lib/aws-sdk-s3/encryption.rb +4 -0
  45. data/lib/aws-sdk-s3/encryptionV2/client.rb +570 -0
  46. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +223 -0
  47. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
  48. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
  49. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +65 -0
  50. data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
  51. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
  52. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
  53. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
  54. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
  55. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +173 -0
  56. data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
  57. data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
  58. data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
  59. data/lib/aws-sdk-s3/endpoint_parameters.rb +181 -0
  60. data/lib/aws-sdk-s3/endpoint_provider.rb +592 -0
  61. data/lib/aws-sdk-s3/endpoints.rb +1392 -0
  62. data/lib/aws-sdk-s3/errors.rb +126 -1
  63. data/lib/aws-sdk-s3/event_streams.rb +8 -1
  64. data/lib/aws-sdk-s3/express_credentials.rb +55 -0
  65. data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
  66. data/lib/aws-sdk-s3/file_downloader.rb +176 -44
  67. data/lib/aws-sdk-s3/file_part.rb +11 -6
  68. data/lib/aws-sdk-s3/file_uploader.rb +39 -18
  69. data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
  70. data/lib/aws-sdk-s3/multipart_file_uploader.rb +82 -23
  71. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +61 -21
  72. data/lib/aws-sdk-s3/multipart_upload.rb +265 -32
  73. data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
  74. data/lib/aws-sdk-s3/multipart_upload_part.rb +367 -45
  75. data/lib/aws-sdk-s3/object.rb +2475 -228
  76. data/lib/aws-sdk-s3/object_acl.rb +103 -25
  77. data/lib/aws-sdk-s3/object_copier.rb +9 -5
  78. data/lib/aws-sdk-s3/object_multipart_copier.rb +48 -22
  79. data/lib/aws-sdk-s3/object_summary.rb +2075 -203
  80. data/lib/aws-sdk-s3/object_version.rb +492 -80
  81. data/lib/aws-sdk-s3/plugins/accelerate.rb +17 -64
  82. data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
  83. data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
  84. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +7 -43
  85. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +20 -3
  86. data/lib/aws-sdk-s3/plugins/dualstack.rb +7 -50
  87. data/lib/aws-sdk-s3/plugins/endpoints.rb +86 -0
  88. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +5 -4
  89. data/lib/aws-sdk-s3/plugins/express_session_auth.rb +97 -0
  90. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
  91. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +60 -15
  92. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +44 -0
  93. data/lib/aws-sdk-s3/plugins/location_constraint.rb +5 -1
  94. data/lib/aws-sdk-s3/plugins/md5s.rb +35 -30
  95. data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
  96. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
  97. data/lib/aws-sdk-s3/plugins/s3_signer.rb +63 -94
  98. data/lib/aws-sdk-s3/plugins/skip_whole_multipart_get_checksums.rb +31 -0
  99. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
  100. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +139 -0
  101. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
  102. data/lib/aws-sdk-s3/presigned_post.rb +160 -99
  103. data/lib/aws-sdk-s3/presigner.rb +138 -59
  104. data/lib/aws-sdk-s3/resource.rb +155 -17
  105. data/lib/aws-sdk-s3/types.rb +12229 -4377
  106. data/lib/aws-sdk-s3/waiters.rb +67 -1
  107. data/lib/aws-sdk-s3.rb +46 -32
  108. data/sig/bucket.rbs +216 -0
  109. data/sig/bucket_acl.rbs +78 -0
  110. data/sig/bucket_cors.rbs +69 -0
  111. data/sig/bucket_lifecycle.rbs +88 -0
  112. data/sig/bucket_lifecycle_configuration.rbs +115 -0
  113. data/sig/bucket_logging.rbs +76 -0
  114. data/sig/bucket_notification.rbs +114 -0
  115. data/sig/bucket_policy.rbs +59 -0
  116. data/sig/bucket_request_payment.rbs +54 -0
  117. data/sig/bucket_tagging.rbs +65 -0
  118. data/sig/bucket_versioning.rbs +77 -0
  119. data/sig/bucket_website.rbs +93 -0
  120. data/sig/client.rbs +2406 -0
  121. data/sig/customizations/bucket.rbs +19 -0
  122. data/sig/customizations/object.rbs +38 -0
  123. data/sig/customizations/object_summary.rbs +35 -0
  124. data/sig/errors.rbs +34 -0
  125. data/sig/multipart_upload.rbs +111 -0
  126. data/sig/multipart_upload_part.rbs +105 -0
  127. data/sig/object.rbs +443 -0
  128. data/sig/object_acl.rbs +86 -0
  129. data/sig/object_summary.rbs +335 -0
  130. data/sig/object_version.rbs +137 -0
  131. data/sig/resource.rbs +132 -0
  132. data/sig/types.rbs +2596 -0
  133. data/sig/waiters.rbs +95 -0
  134. metadata +74 -15
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+ module Plugins
6
+ # @api private
7
+ class ExpressSessionAuth < Seahorse::Client::Plugin
8
+ # This should be s3_disable_express_auth instead
9
+ # But this is not a built in. We're overwriting the generated value
10
+ option(:disable_s3_express_session_auth,
11
+ default: false,
12
+ doc_type: 'Boolean',
13
+ docstring: <<-DOCS) do |cfg|
14
+ When `true`, S3 Express session authentication is disabled.
15
+ DOCS
16
+ resolve_disable_s3_express_session_auth(cfg)
17
+ end
18
+
19
+ option(:express_credentials_provider,
20
+ doc_type: 'Aws::S3::ExpressCredentialsProvider',
21
+ rbs_type: 'untyped',
22
+ docstring: <<-DOCS) do |_cfg|
23
+ Credential Provider for S3 Express endpoints. Manages credentials
24
+ for different buckets.
25
+ DOCS
26
+ Aws::S3::ExpressCredentialsProvider.new
27
+ end
28
+
29
+ # @api private
30
+ class Handler < Seahorse::Client::Handler
31
+ def call(context)
32
+ if (props = context[:endpoint_properties])
33
+ # S3 Express endpoint - turn off md5 and enable crc32 default
34
+ if props['backend'] == 'S3Express'
35
+ if context.operation_name == :put_object || checksum_required?(context)
36
+ context[:default_request_checksum_algorithm] = 'CRC32'
37
+ end
38
+ context[:s3_express_endpoint] = true
39
+ end
40
+
41
+ # if s3 express auth, use new credentials and sign additional header
42
+ if context[:auth_scheme]['name'] == 'sigv4-s3express' &&
43
+ !context.config.disable_s3_express_session_auth
44
+ bucket = context.params[:bucket]
45
+ credentials_provider = context.config.express_credentials_provider
46
+ credentials = credentials_provider.express_credentials_for(bucket)
47
+ context[:sigv4_credentials] = credentials # Sign will use this
48
+ end
49
+ end
50
+ with_metric(credentials) { @handler.call(context) }
51
+ end
52
+
53
+ private
54
+
55
+ def with_metric(credentials, &block)
56
+ return block.call unless credentials
57
+
58
+ Aws::Plugins::UserAgent.metric('S3_EXPRESS_BUCKET', &block)
59
+ end
60
+
61
+ def checksum_required?(context)
62
+ context.operation.http_checksum_required ||
63
+ (context.operation.http_checksum &&
64
+ context.operation.http_checksum['requestChecksumRequired'])
65
+ end
66
+ end
67
+
68
+ handler(Handler)
69
+
70
+ # Optimization - sets this client as the client to create sessions.
71
+ def after_initialize(client)
72
+ provider = client.config.express_credentials_provider
73
+ provider.client = client unless provider.client
74
+ end
75
+
76
+ class << self
77
+ private
78
+
79
+ def resolve_disable_s3_express_session_auth(cfg)
80
+ value = ENV['AWS_S3_DISABLE_EXPRESS_SESSION_AUTH'] ||
81
+ Aws.shared_config.s3_disable_express_session_auth(profile: cfg.profile) ||
82
+ 'false'
83
+ value = Aws::Util.str_2_bool(value)
84
+ # Raise if provided value is not true or false
85
+ if value.nil?
86
+ raise ArgumentError,
87
+ 'Must provide either `true` or `false` for the '\
88
+ '`s3_disable_express_session_auth` profile option or for '\
89
+ "ENV['AWS_S3_DISABLE_EXPRESS_SESSION_AUTH']."
90
+ end
91
+ value
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Plugins
@@ -9,7 +11,7 @@ module Aws
9
11
  @handler.call(context).on(200) do |response|
10
12
  response.data = S3::Types::GetBucketLocationOutput.new
11
13
  xml = context.http_response.body_contents
12
- matches = xml.match(/>(.+?)<\/LocationConstraint>/)
14
+ matches = xml.match(/<LocationConstraint.*?>(.+?)<\/LocationConstraint>/)
13
15
  response.data[:location_constraint] = matches ? matches[1] : ''
14
16
  end
15
17
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Plugins
@@ -13,34 +15,77 @@ module Aws
13
15
 
14
16
  def call(context)
15
17
  @handler.call(context).on(200) do |response|
16
- if error = check_for_error(context)
17
- context.http_response.status_code = 500
18
- response.data = nil
19
- response.error = error
18
+ return response if streaming_output?(context.operation.output)
19
+
20
+ error = check_for_error(context)
21
+ return response unless error
22
+
23
+ context.http_response.status_code = 500
24
+ response.data = nil
25
+ response.error = error
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # Streaming outputs are not subject to 200 errors.
32
+ def streaming_output?(output)
33
+ if (payload = output[:payload_member])
34
+ # checking ref and shape
35
+ payload['streaming'] || payload.shape['streaming'] ||
36
+ payload.eventstream
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ # Checks if the output shape is a structure shape and has members that
43
+ # are in the body for the case of a payload and a normal structure. A
44
+ # non-structure shape will not have members in the body. In the case
45
+ # of a string or blob, the body contents would have been checked first
46
+ # before this method is called in incomplete_xml_body?.
47
+ def members_in_body?(output)
48
+ shape =
49
+ if output[:payload_member]
50
+ output[:payload_member].shape
51
+ else
52
+ output.shape
20
53
  end
54
+
55
+ if structure_shape?(shape)
56
+ shape.members.any? { |_, k| k.location.nil? }
57
+ else
58
+ false
21
59
  end
22
60
  end
23
61
 
62
+ def structure_shape?(shape)
63
+ shape.is_a?(Seahorse::Model::Shapes::StructureShape)
64
+ end
65
+
66
+ # Must have a member in the body and have the start of an XML Tag.
67
+ # Other incomplete xml bodies will result in an XML ParsingError.
68
+ def incomplete_xml_body?(xml, output)
69
+ members_in_body?(output) && !xml.match(/<\w/)
70
+ end
71
+
24
72
  def check_for_error(context)
25
73
  xml = context.http_response.body_contents
26
- if xml.match(/<Error>/)
74
+ if xml.match(/\?>\s*<Error>/)
27
75
  error_code = xml.match(/<Code>(.+?)<\/Code>/)[1]
28
76
  error_message = xml.match(/<Message>(.+?)<\/Message>/)[1]
29
77
  S3::Errors.error_class(error_code).new(context, error_message)
78
+ elsif incomplete_xml_body?(xml, context.operation.output)
79
+ Seahorse::Client::NetworkingError.new(
80
+ S3::Errors
81
+ .error_class('InternalError')
82
+ .new(context, 'Empty or incomplete response body')
83
+ )
30
84
  end
31
85
  end
32
-
33
86
  end
34
87
 
35
- handler(Handler,
36
- step: :sign,
37
- operations: [
38
- :complete_multipart_upload,
39
- :copy_object,
40
- :upload_part_copy,
41
- ]
42
- )
43
-
88
+ handler(Handler, step: :sign)
44
89
  end
45
90
  end
46
91
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+ module Plugins
6
+
7
+ class IADRegionalEndpoint < Seahorse::Client::Plugin
8
+
9
+ option(:s3_us_east_1_regional_endpoint,
10
+ default: 'legacy',
11
+ doc_type: String,
12
+ docstring: <<-DOCS) do |cfg|
13
+ Pass in `regional` to enable the `us-east-1` regional endpoint.
14
+ Defaults to `legacy` mode which uses the global endpoint.
15
+ DOCS
16
+ resolve_iad_regional_endpoint(cfg)
17
+ end
18
+
19
+ private
20
+
21
+ def self.resolve_iad_regional_endpoint(cfg)
22
+ default_mode_value =
23
+ if cfg.respond_to?(:defaults_mode_config_resolver)
24
+ cfg.defaults_mode_config_resolver.resolve(:s3_us_east_1_regional_endpoint)
25
+ end
26
+
27
+ mode = ENV['AWS_S3_US_EAST_1_REGIONAL_ENDPOINT'] ||
28
+ Aws.shared_config.s3_us_east_1_regional_endpoint(profile: cfg.profile) ||
29
+ default_mode_value ||
30
+ 'legacy'
31
+ mode = mode.downcase
32
+ unless %w(legacy regional).include?(mode)
33
+ raise ArgumentError, "expected :s3_us_east_1_regional_endpoint or"\
34
+ " ENV['AWS_S3_US_EAST_1_REGIONAL_ENDPOINT'] to be `legacy` or"\
35
+ " `regional`."
36
+ end
37
+ mode
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Plugins
@@ -20,7 +22,9 @@ module Aws
20
22
 
21
23
  def populate_location_constraint(params, region)
22
24
  params[:create_bucket_configuration] ||= {}
23
- params[:create_bucket_configuration][:location_constraint] ||= region
25
+ unless params[:create_bucket_configuration][:location]
26
+ params[:create_bucket_configuration][:location_constraint] ||= region
27
+ end
24
28
  end
25
29
 
26
30
  end
@@ -1,21 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
- require 'base64'
3
4
 
4
5
  module Aws
5
6
  module S3
6
7
  module Plugins
8
+ # @api private
9
+ # This plugin is effectively deprecated in favor of modeled
10
+ # httpChecksumRequired traits.
7
11
  class Md5s < Seahorse::Client::Plugin
8
-
9
- # Amazon S3 requires these operations to have an MD5 checksum
10
- REQUIRED_OPERATIONS = [
11
- :delete_objects,
12
- :put_bucket_cors,
13
- :put_bucket_lifecycle,
14
- :put_bucket_policy,
15
- :put_bucket_tagging,
16
- :put_object_legal_hold,
17
- :put_object_lock_configuration,
18
- :put_object_retention
12
+ # These operations allow Content MD5 but are not required by
13
+ # httpChecksumRequired. This list should not grow.
14
+ OPTIONAL_OPERATIONS = [
15
+ :put_object,
16
+ :upload_part
19
17
  ]
20
18
 
21
19
  # @api private
@@ -24,9 +22,12 @@ module Aws
24
22
  CHUNK_SIZE = 1 * 1024 * 1024 # one MB
25
23
 
26
24
  def call(context)
27
- body = context.http_request.body
28
- if body.size > 0
29
- context.http_request.headers['Content-Md5'] ||= md5(body)
25
+ if !context[:checksum_algorithms] && # skip in favor of flexible checksum
26
+ !context[:s3_express_endpoint] # s3 express endpoints do not support md5
27
+ body = context.http_request.body
28
+ if body.respond_to?(:size) && body.size > 0
29
+ context.http_request.headers['Content-Md5'] ||= md5(body)
30
+ end
30
31
  end
31
32
  @handler.call(context)
32
33
  end
@@ -37,18 +38,20 @@ module Aws
37
38
  # @return [String<MD5>]
38
39
  def md5(value)
39
40
  if (File === value || Tempfile === value) && !value.path.nil? && File.exist?(value.path)
40
- Base64.encode64(OpenSSL::Digest::MD5.file(value).digest).strip
41
+ OpenSSL::Digest::MD5.file(value).base64digest
41
42
  elsif value.respond_to?(:read)
42
43
  md5 = OpenSSL::Digest::MD5.new
43
44
  update_in_chunks(md5, value)
44
- Base64.encode64(md5.digest).strip
45
+ md5.base64digest
45
46
  else
46
- Base64.encode64(OpenSSL::Digest::MD5.digest(value)).strip
47
+ OpenSSL::Digest::MD5.digest(value).base64digest
47
48
  end
48
49
  end
49
50
 
50
51
  def update_in_chunks(digest, io)
51
- while chunk = io.read(CHUNK_SIZE, buffer ||= "")
52
+ loop do
53
+ chunk = io.read(CHUNK_SIZE)
54
+ break unless chunk
52
55
  digest.update(chunk)
53
56
  end
54
57
  io.rewind
@@ -60,20 +63,22 @@ module Aws
60
63
  default: true,
61
64
  doc_type: 'Boolean',
62
65
  docstring: <<-DOCS)
63
- When `true` a MD5 checksum will be computed for every request that
64
- sends a body. When `false`, MD5 checksums will only be computed
65
- for operations that require them. Checksum errors returned by Amazon
66
- S3 are automatically retried up to `:retry_limit` times.
66
+ When `true` a MD5 checksum will be computed and sent in the Content Md5
67
+ header for :put_object and :upload_part. When `false`, MD5 checksums
68
+ will not be computed for these operations. Checksums are still computed
69
+ for operations requiring them. Checksum errors returned by Amazon S3 are
70
+ automatically retried up to `:retry_limit` times.
67
71
  DOCS
68
72
 
69
73
  def add_handlers(handlers, config)
70
- # priority set low to ensure md5 is computed AFTER the request is
71
- # built but before it is signed
72
- handlers.add(Handler, {
73
- priority: 10,
74
- step: :build,
75
- operations: config.compute_checksums ? nil : REQUIRED_OPERATIONS,
76
- })
74
+ if config.compute_checksums
75
+ # priority set low to ensure md5 is computed AFTER the request is
76
+ # built but before it is signed
77
+ handlers.add(
78
+ Handler,
79
+ priority: 10, step: :build, operations: OPTIONAL_OPERATIONS
80
+ )
81
+ end
77
82
  end
78
83
 
79
84
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Plugins
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Plugins
@@ -1,25 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aws-sigv4'
2
4
 
3
5
  module Aws
4
6
  module S3
7
+ # @api private
8
+ def self.bucket_region_cache
9
+ @bucket_region_cache ||= BucketRegionCache.new
10
+ end
11
+
5
12
  module Plugins
6
- # This plugin is an implementation detail and may be modified.
13
+ # This plugin used to have a V4 signer but it was removed in favor of
14
+ # generic Sign plugin that uses endpoint auth scheme.
15
+ #
7
16
  # @api private
8
17
  class S3Signer < Seahorse::Client::Plugin
9
-
10
18
  option(:signature_version, 'v4')
11
19
 
12
- option(:sigv4_signer) do |cfg|
13
- S3Signer.build_v4_signer(
14
- region: cfg.sigv4_region,
15
- credentials: cfg.credentials
16
- )
17
- end
18
-
19
- option(:sigv4_region) do |cfg|
20
- Aws::Partitions::EndpointProvider.signing_region(cfg.region, 's3')
21
- end
22
-
23
20
  def add_handlers(handlers, cfg)
24
21
  case cfg.signature_version
25
22
  when 'v4' then add_v4_handlers(handlers)
@@ -32,11 +29,11 @@ module Aws
32
29
 
33
30
  def add_v4_handlers(handlers)
34
31
  handlers.add(CachedBucketRegionHandler, step: :sign, priority: 60)
35
- handlers.add(V4Handler, step: :sign)
36
32
  handlers.add(BucketRegionErrorHandler, step: :sign, priority: 40)
37
33
  end
38
34
 
39
35
  def add_legacy_handler(handlers)
36
+ # generic Sign plugin will be skipped if it sees sigv2
40
37
  handlers.add(LegacyHandler, step: :sign)
41
38
  end
42
39
 
@@ -47,40 +44,9 @@ module Aws
47
44
  end
48
45
  end
49
46
 
50
- class V4Handler < Seahorse::Client::Handler
51
-
52
- def call(context)
53
- Aws::Plugins::SignatureV4.apply_signature(
54
- context: context,
55
- signer: sigv4_signer(context)
56
- )
57
- @handler.call(context)
58
- end
59
-
60
- private
61
-
62
- def sigv4_signer(context)
63
- # If the client was configured with the wrong region,
64
- # we have to build a new signer.
65
- if
66
- context[:cached_sigv4_region] &&
67
- context[:cached_sigv4_region] != context.config.sigv4_signer.region
68
- then
69
- S3Signer.build_v4_signer(
70
- region: context[:cached_sigv4_region],
71
- credentials: context.config.credentials
72
- )
73
- else
74
- context.config.sigv4_signer
75
- end
76
- end
77
-
78
- end
79
-
80
47
  # This handler will update the http endpoint when the bucket region
81
48
  # is known/cached.
82
49
  class CachedBucketRegionHandler < Seahorse::Client::Handler
83
-
84
50
  def call(context)
85
51
  bucket = context.params[:bucket]
86
52
  check_for_cached_region(context, bucket) if bucket
@@ -90,21 +56,24 @@ module Aws
90
56
  private
91
57
 
92
58
  def check_for_cached_region(context, bucket)
93
- cached_region = S3::BUCKET_REGIONS[bucket]
94
- if cached_region && cached_region != context.config.region
95
- context.http_request.endpoint.host = S3Signer.new_hostname(context, cached_region)
96
- context[:cached_sigv4_region] = cached_region
59
+ cached_region = Aws::S3.bucket_region_cache[bucket]
60
+ if cached_region &&
61
+ cached_region != context.config.region &&
62
+ !S3Signer.custom_endpoint?(context)
63
+ context.http_request.endpoint.host = S3Signer.new_hostname(
64
+ context, cached_region
65
+ )
66
+ context[:sigv4_region] = cached_region # Sign plugin will use this
97
67
  end
98
68
  end
99
-
100
69
  end
101
70
 
102
71
  # This handler detects when a request fails because of a mismatched bucket
103
72
  # region. It follows up by making a request to determine the correct
104
73
  # region, then finally a version 4 signed request against the correct
105
- # regional endpoint.
74
+ # regional endpoint. This is intended for s3's global endpoint which
75
+ # will return 400 if the bucket is not in region.
106
76
  class BucketRegionErrorHandler < Seahorse::Client::Handler
107
-
108
77
  def call(context)
109
78
  response = @handler.call(context)
110
79
  handle_region_errors(response)
@@ -113,7 +82,10 @@ module Aws
113
82
  private
114
83
 
115
84
  def handle_region_errors(response)
116
- if wrong_sigv4_region?(response) && !fips_region?(response)
85
+ if wrong_sigv4_region?(response) &&
86
+ !fips_region?(response) &&
87
+ !S3Signer.custom_endpoint?(response.context) &&
88
+ !expired_credentials?(response)
117
89
  get_region_and_retry(response.context)
118
90
  else
119
91
  response
@@ -130,37 +102,42 @@ module Aws
130
102
  end
131
103
 
132
104
  def update_bucket_cache(context, actual_region)
133
- S3::BUCKET_REGIONS[context.params[:bucket]] = actual_region
105
+ Aws::S3.bucket_region_cache[context.params[:bucket]] = actual_region
134
106
  end
135
107
 
136
108
  def fips_region?(resp)
137
- resp.context.http_request.endpoint.host.include?('fips')
109
+ resp.context.http_request.endpoint.host.include?('s3-fips.')
110
+ end
111
+
112
+ def expired_credentials?(resp)
113
+ resp.context.http_response.body_contents.match(/<Code>ExpiredToken<\/Code>/)
138
114
  end
139
115
 
140
116
  def wrong_sigv4_region?(resp)
141
117
  resp.context.http_response.status_code == 400 &&
142
- (
143
- resp.context.http_response.headers['x-amz-bucket-region'] ||
144
- resp.context.http_response.body_contents.match(/<Region>.+?<\/Region>/)
145
- )
118
+ (resp.context.http_response.headers['x-amz-bucket-region'] ||
119
+ resp.context.http_response.body_contents.match(/<Region>.+?<\/Region>/))
146
120
  end
147
121
 
148
122
  def resign_with_new_region(context, actual_region)
149
123
  context.http_response.body.truncate(0)
150
- context.http_request.endpoint.host = S3Signer.new_hostname(context, actual_region)
124
+ context.http_request.endpoint.host = S3Signer.new_hostname(
125
+ context, actual_region
126
+ )
151
127
  context.metadata[:redirect_region] = actual_region
152
- Aws::Plugins::SignatureV4.apply_signature(
153
- context: context,
154
- signer: S3Signer.build_v4_signer(
155
- region: actual_region,
156
- credentials: context.config.credentials
157
- )
128
+
129
+ signer = Aws::Plugins::Sign.signer_for(
130
+ context[:auth_scheme],
131
+ context.config,
132
+ actual_region
158
133
  )
134
+
135
+ signer.sign(context)
159
136
  end
160
137
 
161
138
  def region_from_body(body)
162
139
  region = body.match(/<Region>(.+?)<\/Region>/)[1]
163
- if region.nil? || region == ""
140
+ if region.nil? || region == ''
164
141
  raise "couldn't get region from body: #{body}"
165
142
  else
166
143
  region
@@ -168,44 +145,36 @@ module Aws
168
145
  end
169
146
 
170
147
  def log_warning(context, actual_region)
171
- msg = "S3 client configured for #{context.config.region.inspect} " +
172
- "but the bucket #{context.params[:bucket].inspect} is in " +
173
- "#{actual_region.inspect}; Please configure the proper region " +
174
- "to avoid multiple unnecessary redirects and signing attempts\n"
175
- if logger = context.config.logger
148
+ msg = "S3 client configured for #{context.config.region.inspect} " \
149
+ "but the bucket #{context.params[:bucket].inspect} is in " \
150
+ "#{actual_region.inspect}; Please configure the proper region " \
151
+ "to avoid multiple unnecessary redirects and signing attempts\n"
152
+ if (logger = context.config.logger)
176
153
  logger.warn(msg)
177
154
  else
178
155
  warn(msg)
179
156
  end
180
157
  end
181
-
182
158
  end
183
159
 
184
160
  class << self
185
-
186
- # @option options [required, String] :region
187
- # @option options [required, #credentials] :credentials
188
- # @api private
189
- def build_v4_signer(options = {})
190
- Aws::Sigv4::Signer.new({
191
- service: 's3',
192
- region: options[:region],
193
- credentials_provider: options[:credentials],
194
- uri_escape_path: false,
195
- unsigned_headers: ['content-length', 'x-amzn-trace-id'],
196
- })
197
- end
198
-
199
161
  def new_hostname(context, region)
200
- bucket = context.params[:bucket]
201
- if region == 'us-east-1'
202
- "#{bucket}.s3.amazonaws.com"
203
- else
204
- endpoint = Aws::Partitions::EndpointProvider.resolve(region, 's3')
205
- bucket + '.' + URI.parse(endpoint).host
206
- end
162
+ endpoint_params = context[:endpoint_params].dup
163
+ endpoint_params.region = region
164
+ endpoint_params.endpoint = nil
165
+ endpoint =
166
+ context.config.endpoint_provider.resolve_endpoint(endpoint_params)
167
+ URI(endpoint.url).host
207
168
  end
208
169
 
170
+ def custom_endpoint?(context)
171
+ region = context.config.region
172
+ partition = Aws::Endpoints::Matchers.aws_partition(region)
173
+ endpoint = context.http_request.endpoint
174
+
175
+ !endpoint.hostname.include?(partition['dnsSuffix']) &&
176
+ !endpoint.hostname.include?(partition['dualStackDnsSuffix'])
177
+ end
209
178
  end
210
179
  end
211
180
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+ module Plugins
6
+
7
+ # S3 GetObject results for whole Multipart Objects contain a checksum
8
+ # that cannot be validated. These should be skipped by the
9
+ # ChecksumAlgorithm plugin.
10
+ class SkipWholeMultipartGetChecksums < Seahorse::Client::Plugin
11
+
12
+ class Handler < Seahorse::Client::Handler
13
+
14
+ def call(context)
15
+ context[:http_checksum] ||= {}
16
+ context[:http_checksum][:skip_on_suffix] = true
17
+
18
+ @handler.call(context)
19
+ end
20
+
21
+ end
22
+
23
+ handler(
24
+ Handler,
25
+ step: :initialize,
26
+ operations: [:get_object]
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end