aws-sdk-core 3.124.0 → 3.186.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +552 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-defaults/default_configuration.rb +153 -0
  5. data/lib/aws-defaults/defaults_mode_config_resolver.rb +107 -0
  6. data/lib/aws-defaults.rb +3 -0
  7. data/lib/aws-sdk-core/arn.rb +13 -0
  8. data/lib/aws-sdk-core/assume_role_credentials.rb +21 -7
  9. data/lib/aws-sdk-core/assume_role_web_identity_credentials.rb +14 -10
  10. data/lib/aws-sdk-core/binary/encode_handler.rb +12 -1
  11. data/lib/aws-sdk-core/client_stubs.rb +5 -1
  12. data/lib/aws-sdk-core/credential_provider.rb +3 -0
  13. data/lib/aws-sdk-core/credential_provider_chain.rb +8 -5
  14. data/lib/aws-sdk-core/ec2_metadata.rb +3 -2
  15. data/lib/aws-sdk-core/ecs_credentials.rb +121 -53
  16. data/lib/aws-sdk-core/endpoints/condition.rb +41 -0
  17. data/lib/aws-sdk-core/endpoints/endpoint.rb +17 -0
  18. data/lib/aws-sdk-core/endpoints/endpoint_rule.rb +75 -0
  19. data/lib/aws-sdk-core/endpoints/error_rule.rb +42 -0
  20. data/lib/aws-sdk-core/endpoints/function.rb +80 -0
  21. data/lib/aws-sdk-core/endpoints/matchers.rb +131 -0
  22. data/lib/aws-sdk-core/endpoints/reference.rb +31 -0
  23. data/lib/aws-sdk-core/endpoints/rule.rb +25 -0
  24. data/lib/aws-sdk-core/endpoints/rule_set.rb +52 -0
  25. data/lib/aws-sdk-core/endpoints/rules_provider.rb +37 -0
  26. data/lib/aws-sdk-core/endpoints/templater.rb +58 -0
  27. data/lib/aws-sdk-core/endpoints/tree_rule.rb +45 -0
  28. data/lib/aws-sdk-core/endpoints/url.rb +60 -0
  29. data/lib/aws-sdk-core/endpoints.rb +78 -0
  30. data/lib/aws-sdk-core/errors.rb +15 -2
  31. data/lib/aws-sdk-core/ini_parser.rb +7 -0
  32. data/lib/aws-sdk-core/instance_profile_credentials.rb +124 -31
  33. data/lib/aws-sdk-core/json/error_handler.rb +20 -1
  34. data/lib/aws-sdk-core/json/parser.rb +1 -1
  35. data/lib/aws-sdk-core/log/formatter.rb +6 -0
  36. data/lib/aws-sdk-core/pageable_response.rb +81 -32
  37. data/lib/aws-sdk-core/param_validator.rb +2 -2
  38. data/lib/aws-sdk-core/plugins/bearer_authorization.rb +67 -0
  39. data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +340 -0
  40. data/lib/aws-sdk-core/plugins/credentials_configuration.rb +24 -0
  41. data/lib/aws-sdk-core/plugins/defaults_mode.rb +40 -0
  42. data/lib/aws-sdk-core/plugins/endpoint_discovery.rb +6 -2
  43. data/lib/aws-sdk-core/plugins/http_checksum.rb +8 -1
  44. data/lib/aws-sdk-core/plugins/jsonvalue_converter.rb +34 -6
  45. data/lib/aws-sdk-core/plugins/recursion_detection.rb +38 -0
  46. data/lib/aws-sdk-core/plugins/regional_endpoint.rb +111 -30
  47. data/lib/aws-sdk-core/plugins/request_compression.rb +217 -0
  48. data/lib/aws-sdk-core/plugins/response_paging.rb +1 -1
  49. data/lib/aws-sdk-core/plugins/retries/error_inspector.rb +7 -4
  50. data/lib/aws-sdk-core/plugins/retry_errors.rb +21 -5
  51. data/lib/aws-sdk-core/plugins/sign.rb +201 -0
  52. data/lib/aws-sdk-core/plugins/signature_v2.rb +1 -0
  53. data/lib/aws-sdk-core/plugins/signature_v4.rb +15 -8
  54. data/lib/aws-sdk-core/plugins/stub_responses.rb +5 -1
  55. data/lib/aws-sdk-core/plugins/user_agent.rb +117 -14
  56. data/lib/aws-sdk-core/process_credentials.rb +9 -11
  57. data/lib/aws-sdk-core/refreshing_credentials.rb +41 -16
  58. data/lib/aws-sdk-core/refreshing_token.rb +71 -0
  59. data/lib/aws-sdk-core/rest/handler.rb +1 -1
  60. data/lib/aws-sdk-core/rest/request/headers.rb +11 -8
  61. data/lib/aws-sdk-core/rest/request/querystring_builder.rb +43 -29
  62. data/lib/aws-sdk-core/shared_config.rb +108 -12
  63. data/lib/aws-sdk-core/sso_credentials.rb +91 -50
  64. data/lib/aws-sdk-core/sso_token_provider.rb +135 -0
  65. data/lib/aws-sdk-core/static_token_provider.rb +14 -0
  66. data/lib/aws-sdk-core/structure.rb +6 -4
  67. data/lib/aws-sdk-core/stubbing/stub_data.rb +11 -0
  68. data/lib/aws-sdk-core/token.rb +31 -0
  69. data/lib/aws-sdk-core/token_provider.rb +15 -0
  70. data/lib/aws-sdk-core/token_provider_chain.rb +51 -0
  71. data/lib/aws-sdk-core/waiters/poller.rb +3 -1
  72. data/lib/aws-sdk-core/xml/error_handler.rb +7 -0
  73. data/lib/aws-sdk-core/xml/parser/engines/oga.rb +2 -0
  74. data/lib/aws-sdk-core/xml/parser/engines/ox.rb +1 -1
  75. data/lib/aws-sdk-core.rb +17 -0
  76. data/lib/aws-sdk-sso/client.rb +85 -12
  77. data/lib/aws-sdk-sso/endpoint_parameters.rb +66 -0
  78. data/lib/aws-sdk-sso/endpoint_provider.rb +57 -0
  79. data/lib/aws-sdk-sso/endpoints.rb +72 -0
  80. data/lib/aws-sdk-sso/plugins/endpoints.rb +76 -0
  81. data/lib/aws-sdk-sso/types.rb +8 -43
  82. data/lib/aws-sdk-sso.rb +5 -1
  83. data/lib/aws-sdk-ssooidc/client.rb +626 -0
  84. data/lib/aws-sdk-ssooidc/client_api.rb +216 -0
  85. data/lib/aws-sdk-ssooidc/customizations.rb +1 -0
  86. data/lib/aws-sdk-ssooidc/endpoint_parameters.rb +66 -0
  87. data/lib/aws-sdk-ssooidc/endpoint_provider.rb +57 -0
  88. data/lib/aws-sdk-ssooidc/endpoints.rb +58 -0
  89. data/lib/aws-sdk-ssooidc/errors.rb +290 -0
  90. data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +74 -0
  91. data/lib/aws-sdk-ssooidc/resource.rb +26 -0
  92. data/lib/aws-sdk-ssooidc/types.rb +502 -0
  93. data/lib/aws-sdk-ssooidc.rb +59 -0
  94. data/lib/aws-sdk-sts/client.rb +332 -263
  95. data/lib/aws-sdk-sts/client_api.rb +12 -1
  96. data/lib/aws-sdk-sts/endpoint_parameters.rb +78 -0
  97. data/lib/aws-sdk-sts/endpoint_provider.rb +112 -0
  98. data/lib/aws-sdk-sts/endpoints.rb +136 -0
  99. data/lib/aws-sdk-sts/plugins/endpoints.rb +84 -0
  100. data/lib/aws-sdk-sts/plugins/sts_regional_endpoints.rb +5 -1
  101. data/lib/aws-sdk-sts/presigner.rb +13 -15
  102. data/lib/aws-sdk-sts/types.rb +121 -204
  103. data/lib/aws-sdk-sts.rb +5 -1
  104. data/lib/seahorse/client/async_base.rb +0 -1
  105. data/lib/seahorse/client/configuration.rb +1 -5
  106. data/lib/seahorse/client/h2/connection.rb +12 -11
  107. data/lib/seahorse/client/net_http/connection_pool.rb +7 -0
  108. data/lib/seahorse/client/plugins/net_http.rb +33 -2
  109. data/lib/seahorse/client/plugins/request_callback.rb +40 -9
  110. data/lib/seahorse/client/response.rb +6 -0
  111. data/lib/seahorse/model/operation.rb +6 -0
  112. data/lib/seahorse/util.rb +4 -0
  113. metadata +62 -8
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sigv4'
4
+
5
+ module Aws
6
+ module Plugins
7
+ # @api private
8
+ class Sign < Seahorse::Client::Plugin
9
+ # These once had defaults. But now they are used as overrides to
10
+ # new endpoint and auth resolution.
11
+ option(:sigv4_signer)
12
+ option(:sigv4_name)
13
+ option(:sigv4_region)
14
+ option(:unsigned_operations, default: [])
15
+
16
+ supported_auth_types = %w[sigv4 bearer none]
17
+ supported_auth_types += ['sigv4a'] if Aws::Sigv4::Signer.use_crt?
18
+ SUPPORTED_AUTH_TYPES = supported_auth_types.freeze
19
+
20
+ def add_handlers(handlers, cfg)
21
+ operations = cfg.api.operation_names - cfg.unsigned_operations
22
+ handlers.add(Handler, step: :sign, operations: operations)
23
+ end
24
+
25
+ # @api private
26
+ # Return a signer with the `sign(context)` method
27
+ def self.signer_for(auth_scheme, config, region_override = nil)
28
+ case auth_scheme['name']
29
+ when 'sigv4', 'sigv4a'
30
+ SignatureV4.new(auth_scheme, config, region_override)
31
+ when 'bearer'
32
+ Bearer.new
33
+ else
34
+ NullSigner.new
35
+ end
36
+ end
37
+
38
+ class Handler < Seahorse::Client::Handler
39
+ def call(context)
40
+ # Skip signing if using sigv2 signing from s3_signer in S3
41
+ unless v2_signing?(context.config)
42
+ signer = Sign.signer_for(
43
+ context[:auth_scheme],
44
+ context.config,
45
+ context[:sigv4_region]
46
+ )
47
+ signer.sign(context)
48
+ end
49
+ @handler.call(context)
50
+ end
51
+
52
+ private
53
+
54
+ def v2_signing?(config)
55
+ # 's3' is legacy signing, 'v4' is default
56
+ config.respond_to?(:signature_version) &&
57
+ config.signature_version == 's3'
58
+ end
59
+ end
60
+
61
+ # @api private
62
+ class Bearer
63
+ def initialize
64
+ end
65
+
66
+ def sign(context)
67
+ if context.http_request.endpoint.scheme != 'https'
68
+ raise ArgumentError,
69
+ 'Unable to use bearer authorization on non https endpoint.'
70
+ end
71
+
72
+ token_provider = context.config.token_provider
73
+
74
+ raise Errors::MissingBearerTokenError unless token_provider&.set?
75
+
76
+ context.http_request.headers['Authorization'] =
77
+ "Bearer #{token_provider.token.token}"
78
+ end
79
+
80
+ def presign_url(*args)
81
+ raise ArgumentError, 'Bearer auth does not support presigned urls'
82
+ end
83
+
84
+ def sign_event(*args)
85
+ raise ArgumentError, 'Bearer auth does not support event signing'
86
+ end
87
+ end
88
+
89
+ # @api private
90
+ class SignatureV4
91
+ def initialize(auth_scheme, config, region_override = nil)
92
+ scheme_name = auth_scheme['name']
93
+
94
+ unless %w[sigv4 sigv4a].include?(scheme_name)
95
+ raise ArgumentError,
96
+ "Expected sigv4 or sigv4a auth scheme, got #{scheme_name}"
97
+ end
98
+
99
+ region = if scheme_name == 'sigv4a'
100
+ auth_scheme['signingRegionSet'].first
101
+ else
102
+ auth_scheme['signingRegion']
103
+ end
104
+ begin
105
+ @signer = Aws::Sigv4::Signer.new(
106
+ service: config.sigv4_name || auth_scheme['signingName'],
107
+ region: region_override || config.sigv4_region || region,
108
+ credentials_provider: config.credentials,
109
+ signing_algorithm: scheme_name.to_sym,
110
+ uri_escape_path: !!!auth_scheme['disableDoubleEncoding'],
111
+ normalize_path: !!!auth_scheme['disableNormalizePath'],
112
+ unsigned_headers: %w[content-length user-agent x-amzn-trace-id]
113
+ )
114
+ rescue Aws::Sigv4::Errors::MissingCredentialsError
115
+ raise Aws::Errors::MissingCredentialsError
116
+ end
117
+ end
118
+
119
+ def sign(context)
120
+ req = context.http_request
121
+
122
+ apply_authtype(context, req)
123
+ reset_signature(req)
124
+ apply_clock_skew(context, req)
125
+
126
+ # compute the signature
127
+ begin
128
+ signature = @signer.sign_request(
129
+ http_method: req.http_method,
130
+ url: req.endpoint,
131
+ headers: req.headers,
132
+ body: req.body
133
+ )
134
+ rescue Aws::Sigv4::Errors::MissingCredentialsError
135
+ # Necessary for when credentials is explicitly set to nil
136
+ raise Aws::Errors::MissingCredentialsError
137
+ end
138
+ # apply signature headers
139
+ req.headers.update(signature.headers)
140
+
141
+ # add request metadata with signature components for debugging
142
+ context[:canonical_request] = signature.canonical_request
143
+ context[:string_to_sign] = signature.string_to_sign
144
+ end
145
+
146
+ def presign_url(*args)
147
+ @signer.presign_url(*args)
148
+ end
149
+
150
+ def sign_event(*args)
151
+ @signer.sign_event(*args)
152
+ end
153
+
154
+ private
155
+
156
+ def apply_authtype(context, req)
157
+ if context.operation['authtype'].eql?('v4-unsigned-body') &&
158
+ req.endpoint.scheme.eql?('https')
159
+ req.headers['X-Amz-Content-Sha256'] ||= 'UNSIGNED-PAYLOAD'
160
+ end
161
+ end
162
+
163
+ def reset_signature(req)
164
+ # in case this request is being re-signed
165
+ req.headers.delete('Authorization')
166
+ req.headers.delete('X-Amz-Security-Token')
167
+ req.headers.delete('X-Amz-Date')
168
+ req.headers.delete('x-Amz-Region-Set')
169
+ end
170
+
171
+ def apply_clock_skew(context, req)
172
+ if context.config.respond_to?(:clock_skew) &&
173
+ context.config.clock_skew &&
174
+ context.config.correct_clock_skew
175
+
176
+ endpoint = context.http_request.endpoint
177
+ skew = context.config.clock_skew.clock_correction(endpoint)
178
+ if skew.abs.positive?
179
+ req.headers['X-Amz-Date'] =
180
+ (Time.now.utc + skew).strftime('%Y%m%dT%H%M%SZ')
181
+ end
182
+ end
183
+ end
184
+
185
+ end
186
+
187
+ # @api private
188
+ class NullSigner
189
+
190
+ def sign(context)
191
+ end
192
+
193
+ def presign_url(*args)
194
+ end
195
+
196
+ def sign_event(*args)
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -3,6 +3,7 @@
3
3
  module Aws
4
4
  module Plugins
5
5
  # @api private
6
+ # Necessary to keep after Endpoints 2.0
6
7
  class SignatureV2 < Seahorse::Client::Plugin
7
8
 
8
9
  option(:v2_signer) do |cfg|
@@ -5,8 +5,11 @@ require 'aws-sigv4'
5
5
  module Aws
6
6
  module Plugins
7
7
  # @api private
8
+ # Necessary to exist after endpoints 2.0
8
9
  class SignatureV4 < Seahorse::Client::Plugin
9
10
 
11
+ V4_AUTH = %w[v4 v4-unsigned-payload v4-unsigned-body]
12
+
10
13
  option(:sigv4_signer) do |cfg|
11
14
  SignatureV4.build_signer(cfg)
12
15
  end
@@ -32,13 +35,16 @@ module Aws
32
35
  end
33
36
 
34
37
  option(:unsigned_operations) do |cfg|
35
- cfg.api.operation_names.inject([]) do |unsigned, operation_name|
36
- if cfg.api.operation(operation_name)['authtype'] == 'none' ||
37
- cfg.api.operation(operation_name)['authtype'] == 'custom'
38
- # Unsign requests that has custom apigateway authorizer as well
39
- unsigned << operation_name
40
- else
41
- unsigned
38
+ if cfg.api.metadata['signatureVersion'] == 'v4'
39
+ # select operations where authtype is set and is not v4
40
+ cfg.api.operation_names.select do |o|
41
+ cfg.api.operation(o)['authtype'] && !V4_AUTH.include?(cfg.api.operation(o)['authtype'])
42
+ end
43
+ else # service is not v4 auth
44
+ # select all operations where authtype is not v4
45
+ # (includes operations with no explicit authtype)
46
+ cfg.api.operation_names.select do |o|
47
+ !V4_AUTH.include?(cfg.api.operation(o)['authtype'])
42
48
  end
43
49
  end
44
50
  end
@@ -98,6 +104,7 @@ module Aws
98
104
  req.headers.delete('Authorization')
99
105
  req.headers.delete('X-Amz-Security-Token')
100
106
  req.headers.delete('X-Amz-Date')
107
+ req.headers.delete('x-Amz-Region-Set')
101
108
 
102
109
  if context.config.respond_to?(:clock_skew) &&
103
110
  context.config.clock_skew &&
@@ -134,7 +141,7 @@ module Aws
134
141
  def apply_authtype(context)
135
142
  if context.operation['authtype'].eql?('v4-unsigned-body') &&
136
143
  context.http_request.endpoint.scheme.eql?('https')
137
- context.http_request.headers['X-Amz-Content-Sha256'] = 'UNSIGNED-PAYLOAD'
144
+ context.http_request.headers['X-Amz-Content-Sha256'] ||= 'UNSIGNED-PAYLOAD'
138
145
  end
139
146
  context
140
147
  end
@@ -51,7 +51,11 @@ requests are made, and retries are disabled.
51
51
  stub = context.client.next_stub(context)
52
52
  resp = Seahorse::Client::Response.new(context: context)
53
53
  async_mode = context.client.is_a? Seahorse::Client::AsyncBase
54
- apply_stub(stub, resp, async_mode)
54
+ if Hash === stub && stub[:mutex]
55
+ stub[:mutex].synchronize { apply_stub(stub, resp, async_mode) }
56
+ else
57
+ apply_stub(stub, resp, async_mode)
58
+ end
55
59
 
56
60
  async_mode ? Seahorse::Client::AsyncResponse.new(
57
61
  context: context, stream: context[:input_event_stream_handler].event_emitter.stream, sync_queue: Queue.new) : resp
@@ -4,7 +4,31 @@ module Aws
4
4
  module Plugins
5
5
  # @api private
6
6
  class UserAgent < Seahorse::Client::Plugin
7
+ # @api private
7
8
  option(:user_agent_suffix)
9
+ # @api private
10
+ option(:user_agent_frameworks, default: [])
11
+
12
+ option(
13
+ :sdk_ua_app_id,
14
+ doc_type: 'String',
15
+ docstring: <<-DOCS) do |cfg|
16
+ A unique and opaque application ID that is appended to the
17
+ User-Agent header as app/<sdk_ua_app_id>. It should have a
18
+ maximum length of 50.
19
+ DOCS
20
+ app_id = ENV['AWS_SDK_UA_APP_ID']
21
+ app_id ||= Aws.shared_config.sdk_ua_app_id(profile: cfg.profile)
22
+ app_id
23
+ end
24
+
25
+ def self.feature(feature, &block)
26
+ Thread.current[:aws_sdk_core_user_agent_feature] ||= []
27
+ Thread.current[:aws_sdk_core_user_agent_feature] << "ft/#{feature}"
28
+ block.call
29
+ ensure
30
+ Thread.current[:aws_sdk_core_user_agent_feature].pop
31
+ end
8
32
 
9
33
  # @api private
10
34
  class Handler < Seahorse::Client::Handler
@@ -14,33 +38,112 @@ module Aws
14
38
  end
15
39
 
16
40
  def set_user_agent(context)
17
- ua = "aws-sdk-ruby3/#{CORE_GEM_VERSION}"
41
+ context.http_request.headers['User-Agent'] = UserAgent.new(context).to_s
42
+ end
43
+
44
+ class UserAgent
45
+ def initialize(context)
46
+ @context = context
47
+ end
48
+
49
+ def to_s
50
+ ua = "aws-sdk-ruby3/#{CORE_GEM_VERSION}"
51
+ ua += ' ua/2.0'
52
+ ua += " #{api_metadata}" if api_metadata
53
+ ua += " #{os_metadata}"
54
+ ua += " #{language_metadata}"
55
+ ua += " #{env_metadata}" if env_metadata
56
+ ua += " #{config_metadata}" if config_metadata
57
+ ua += " #{app_id}" if app_id
58
+ ua += " #{feature_metadata}" if feature_metadata
59
+ ua += " #{framework_metadata}" if framework_metadata
60
+ if @context.config.user_agent_suffix
61
+ ua += " #{@context.config.user_agent_suffix}"
62
+ end
63
+ ua.strip
64
+ end
65
+
66
+ private
18
67
 
19
- begin
20
- ua += " #{RUBY_ENGINE}/#{RUBY_VERSION}"
21
- rescue
22
- ua += " RUBY_ENGINE_NA/#{RUBY_VERSION}"
68
+ # Used to be gem_name/gem_version
69
+ def api_metadata
70
+ service_id = @context.config.api.metadata['serviceId']
71
+ return unless service_id
72
+
73
+ service_id = service_id.gsub(' ', '_').downcase
74
+ gem_version = @context[:gem_version]
75
+ "api/#{service_id}##{gem_version}"
76
+ end
77
+
78
+ # Used to be RUBY_PLATFORM
79
+ def os_metadata
80
+ os =
81
+ case RbConfig::CONFIG['host_os']
82
+ when /mac|darwin/
83
+ 'macos'
84
+ when /linux|cygwin/
85
+ 'linux'
86
+ when /mingw|mswin/
87
+ 'windows'
88
+ else
89
+ 'other'
90
+ end
91
+ metadata = "os/#{os}"
92
+ local_version = Gem::Platform.local.version
93
+ metadata += "##{local_version}" if local_version
94
+ metadata += " md/#{RbConfig::CONFIG['host_cpu']}"
95
+ metadata
23
96
  end
24
97
 
25
- ua += " #{RUBY_PLATFORM}"
98
+ # Used to be RUBY_ENGINE/RUBY_VERSION
99
+ def language_metadata
100
+ "lang/#{RUBY_ENGINE}##{RUBY_ENGINE_VERSION} md/#{RUBY_VERSION}"
101
+ end
102
+
103
+ def env_metadata
104
+ return unless (execution_env = ENV['AWS_EXECUTION_ENV'])
105
+
106
+ "exec-env/#{execution_env}"
107
+ end
26
108
 
27
- if context[:gem_name] && context[:gem_version]
28
- ua += " #{context[:gem_name]}/#{context[:gem_version]}"
109
+ def config_metadata
110
+ "cfg/retry-mode##{@context.config.retry_mode}"
29
111
  end
30
112
 
31
- if (execution_env = ENV['AWS_EXECUTION_ENV'])
32
- ua += " exec-env/#{execution_env}"
113
+ def app_id
114
+ return unless (app_id = @context.config.sdk_ua_app_id)
115
+
116
+ # Sanitize and only allow these characters
117
+ app_id = app_id.gsub(/[^!#$%&'*+\-.^_`|~0-9A-Za-z]/, '-')
118
+ "app/#{app_id}"
33
119
  end
34
120
 
35
- if context.config.user_agent_suffix
36
- ua += " #{context.config.user_agent_suffix}"
121
+ def feature_metadata
122
+ return unless Thread.current[:aws_sdk_core_user_agent_feature]
123
+
124
+ Thread.current[:aws_sdk_core_user_agent_feature].join(' ')
37
125
  end
38
126
 
39
- context.http_request.headers['User-Agent'] = ua.strip
127
+ def framework_metadata
128
+ if (frameworks_cfg = @context.config.user_agent_frameworks).empty?
129
+ return
130
+ end
131
+
132
+ # Frameworks may be aws-record, aws-sdk-rails, etc.
133
+ regex = /gems\/(?<name>#{frameworks_cfg.join('|')})-(?<version>\d+\.\d+\.\d+)/.freeze
134
+ frameworks = {}
135
+ Kernel.caller.each do |line|
136
+ match = line.match(regex)
137
+ next unless match
138
+
139
+ frameworks[match[:name]] = match[:version]
140
+ end
141
+ frameworks.map { |n, v| "lib/#{n}##{v}" }.join(' ')
142
+ end
40
143
  end
41
144
  end
42
145
 
43
- handler(Handler)
146
+ handler(Handler, priority: 1)
44
147
  end
45
148
  end
46
149
  end
@@ -1,19 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aws
4
-
5
4
  # A credential provider that executes a given process and attempts
6
- # to read its stdout to recieve a JSON payload containing the credentials
7
- #
8
- # Automatically handles refreshing credentials if an Expiration time is
9
- # provided in the credentials payload
10
- #
11
- # credentials = Aws::ProcessCredentials.new('/usr/bin/credential_proc').credentials
5
+ # to read its stdout to recieve a JSON payload containing the credentials.
12
6
  #
7
+ # credentials = Aws::ProcessCredentials.new('/usr/bin/credential_proc')
13
8
  # ec2 = Aws::EC2::Client.new(credentials: credentials)
14
9
  #
15
- # More documentation on process based credentials can be found here:
16
- # https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes
10
+ # Automatically handles refreshing credentials if an Expiration time is
11
+ # provided in the credentials payload.
12
+ #
13
+ # @see https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes
17
14
  class ProcessCredentials
18
15
 
19
16
  include CredentialProvider
@@ -27,6 +24,7 @@ module Aws
27
24
  def initialize(process)
28
25
  @process = process
29
26
  @credentials = credentials_from_process(@process)
27
+ @async_refresh = false
30
28
 
31
29
  super
32
30
  end
@@ -73,9 +71,9 @@ module Aws
73
71
  @credentials = credentials_from_process(@process)
74
72
  end
75
73
 
76
- def near_expiration?
74
+ def near_expiration?(expiration_length)
77
75
  # are we within 5 minutes of expiration?
78
- @expiration && (Time.now.to_i + 5 * 60) > @expiration.to_i
76
+ @expiration && (Time.now.to_i + expiration_length) > @expiration.to_i
79
77
  end
80
78
  end
81
79
  end
@@ -17,45 +17,70 @@ module Aws
17
17
  # @api private
18
18
  module RefreshingCredentials
19
19
 
20
+ SYNC_EXPIRATION_LENGTH = 300 # 5 minutes
21
+ ASYNC_EXPIRATION_LENGTH = 600 # 10 minutes
22
+
23
+ CLIENT_EXCLUDE_OPTIONS = Set.new([:before_refresh]).freeze
24
+
20
25
  def initialize(options = {})
21
26
  @mutex = Mutex.new
27
+ @before_refresh = options.delete(:before_refresh) if Hash === options
28
+
29
+ @before_refresh.call(self) if @before_refresh
22
30
  refresh
23
31
  end
24
32
 
25
33
  # @return [Credentials]
26
34
  def credentials
27
- refresh_if_near_expiration
35
+ refresh_if_near_expiration!
28
36
  @credentials
29
37
  end
30
38
 
31
- # @return [Time,nil]
32
- def expiration
33
- refresh_if_near_expiration
34
- @expiration
35
- end
36
-
37
39
  # Refresh credentials.
38
40
  # @return [void]
39
41
  def refresh!
40
- @mutex.synchronize { refresh }
42
+ @mutex.synchronize do
43
+ @before_refresh.call(self) if @before_refresh
44
+
45
+ refresh
46
+ end
41
47
  end
42
48
 
43
49
  private
44
50
 
45
- # Refreshes instance metadata credentials if they are within
46
- # 5 minutes of expiration.
47
- def refresh_if_near_expiration
48
- if near_expiration?
51
+ # Refreshes credentials asynchronously and synchronously.
52
+ # If we are near to expiration, block while getting new credentials.
53
+ # Otherwise, if we're approaching expiration, use the existing credentials
54
+ # but attempt a refresh in the background.
55
+ def refresh_if_near_expiration!
56
+ # Note: This check is an optimization. Rather than acquire the mutex on every #refresh_if_near_expiration
57
+ # call, we check before doing so, and then we check within the mutex to avoid a race condition.
58
+ # See issue: https://github.com/aws/aws-sdk-ruby/issues/2641 for more info.
59
+ if near_expiration?(SYNC_EXPIRATION_LENGTH)
49
60
  @mutex.synchronize do
50
- refresh if near_expiration?
61
+ if near_expiration?(SYNC_EXPIRATION_LENGTH)
62
+ @before_refresh.call(self) if @before_refresh
63
+ refresh
64
+ end
65
+ end
66
+ elsif @async_refresh && near_expiration?(ASYNC_EXPIRATION_LENGTH)
67
+ unless @mutex.locked?
68
+ Thread.new do
69
+ @mutex.synchronize do
70
+ if near_expiration?(ASYNC_EXPIRATION_LENGTH)
71
+ @before_refresh.call(self) if @before_refresh
72
+ refresh
73
+ end
74
+ end
75
+ end
51
76
  end
52
77
  end
53
78
  end
54
79
 
55
- def near_expiration?
80
+ def near_expiration?(expiration_length)
56
81
  if @expiration
57
- # are we within 5 minutes of expiration?
58
- (Time.now.to_i + 5 * 60) > @expiration.to_i
82
+ # Are we within expiration?
83
+ (Time.now.to_i + expiration_length) > @expiration.to_i
59
84
  else
60
85
  true
61
86
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+
5
+ module Aws
6
+
7
+ # Module/mixin used by token provider classes that can be refreshed. This
8
+ # provides basic refresh logic in a thread-safe manner. Classes mixing in
9
+ # this module are expected to implement a #refresh method that populates
10
+ # the following instance variable:
11
+ #
12
+ # * `@token` [Token] - {Aws::Token} object with the `expiration` and `token`
13
+ # fields set.
14
+ #
15
+ # @api private
16
+ module RefreshingToken
17
+
18
+ def initialize(options = {})
19
+ @mutex = Mutex.new
20
+ @before_refresh = options.delete(:before_refresh) if Hash === options
21
+
22
+ @before_refresh.call(self) if @before_refresh
23
+ refresh
24
+ end
25
+
26
+ # @return [Token]
27
+ def token
28
+ refresh_if_near_expiration
29
+ @token
30
+ end
31
+
32
+ # @return [Time,nil]
33
+ def expiration
34
+ refresh_if_near_expiration
35
+ @expiration
36
+ end
37
+
38
+ # Refresh token.
39
+ # @return [void]
40
+ def refresh!
41
+ @mutex.synchronize do
42
+ @before_refresh.call(self) if @before_refresh
43
+ refresh
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # Refreshes token if it is within
50
+ # 5 minutes of expiration.
51
+ def refresh_if_near_expiration
52
+ if near_expiration?
53
+ @mutex.synchronize do
54
+ if near_expiration?
55
+ @before_refresh.call(self) if @before_refresh
56
+ refresh
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def near_expiration?
63
+ if @token && @token.expiration
64
+ # are we within 5 minutes of expiration?
65
+ (Time.now.to_i + 5 * 60) > @token.expiration.to_i
66
+ else
67
+ true
68
+ end
69
+ end
70
+ end
71
+ end
@@ -17,7 +17,7 @@ module Aws
17
17
 
18
18
  def apply_request_id(context)
19
19
  h = context.http_response.headers
20
- context[:request_id] = h['x-amz-request-id'] || h['x-amzn-requestid']
20
+ context[:request_id] ||= h['x-amz-request-id'] || h['x-amzn-requestid']
21
21
  end
22
22
 
23
23
  end
@@ -32,12 +32,11 @@ module Aws
32
32
 
33
33
  def apply_header_value(headers, ref, value)
34
34
  value = apply_json_trait(value) if ref['jsonvalue']
35
- headers[ref.location_name] =
36
- case ref.shape
37
- when TimestampShape then timestamp(ref, value)
38
- when ListShape then list(ref, value)
39
- else value.to_s
40
- end
35
+ case ref.shape
36
+ when TimestampShape then headers[ref.location_name] = timestamp(ref, value)
37
+ when ListShape then list(headers, ref, value)
38
+ else headers[ref.location_name] = value.to_s
39
+ end
41
40
  end
42
41
 
43
42
  def timestamp(ref, value)
@@ -50,8 +49,12 @@ module Aws
50
49
  end
51
50
  end
52
51
 
53
- def list(_ref, value)
54
- value.compact.join(",")
52
+ def list(headers, ref, value)
53
+ return if !value || value.empty?
54
+ headers[ref.location_name] = value
55
+ .compact
56
+ .map { |s| Seahorse::Util.escape_header_list_string(s.to_s) }
57
+ .join(',')
55
58
  end
56
59
 
57
60
  def apply_header_map(headers, ref, values)