aws-sdk-core 3.190.2 → 3.240.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +650 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-defaults.rb +4 -1
  5. data/lib/aws-sdk-core/arn.rb +1 -3
  6. data/lib/aws-sdk-core/assume_role_credentials.rb +21 -13
  7. data/lib/aws-sdk-core/assume_role_web_identity_credentials.rb +16 -9
  8. data/lib/aws-sdk-core/binary/decode_handler.rb +3 -9
  9. data/lib/aws-sdk-core/binary/encode_handler.rb +1 -1
  10. data/lib/aws-sdk-core/binary/event_builder.rb +34 -37
  11. data/lib/aws-sdk-core/binary/event_stream_decoder.rb +1 -0
  12. data/lib/aws-sdk-core/binary/event_stream_encoder.rb +4 -3
  13. data/lib/aws-sdk-core/cbor/decoder.rb +308 -0
  14. data/lib/aws-sdk-core/cbor/encoder.rb +243 -0
  15. data/lib/aws-sdk-core/cbor.rb +53 -0
  16. data/lib/aws-sdk-core/client_side_monitoring.rb +9 -0
  17. data/lib/aws-sdk-core/client_stubs.rb +39 -58
  18. data/lib/aws-sdk-core/credential_provider.rb +5 -1
  19. data/lib/aws-sdk-core/credential_provider_chain.rb +101 -25
  20. data/lib/aws-sdk-core/credentials.rb +19 -6
  21. data/lib/aws-sdk-core/ec2_metadata.rb +1 -1
  22. data/lib/aws-sdk-core/ecs_credentials.rb +16 -14
  23. data/lib/aws-sdk-core/endpoints/endpoint.rb +3 -1
  24. data/lib/aws-sdk-core/endpoints/matchers.rb +8 -10
  25. data/lib/aws-sdk-core/endpoints.rb +101 -21
  26. data/lib/aws-sdk-core/error_handler.rb +46 -0
  27. data/lib/aws-sdk-core/errors.rb +16 -4
  28. data/lib/aws-sdk-core/event_emitter.rb +1 -17
  29. data/lib/aws-sdk-core/instance_profile_credentials.rb +148 -157
  30. data/lib/aws-sdk-core/json/builder.rb +8 -1
  31. data/lib/aws-sdk-core/json/error_handler.rb +29 -13
  32. data/lib/aws-sdk-core/json/handler.rb +6 -6
  33. data/lib/aws-sdk-core/json/json_engine.rb +3 -1
  34. data/lib/aws-sdk-core/json/oj_engine.rb +7 -1
  35. data/lib/aws-sdk-core/json/parser.rb +6 -1
  36. data/lib/aws-sdk-core/json.rb +43 -14
  37. data/lib/aws-sdk-core/log/param_filter.rb +2 -2
  38. data/lib/aws-sdk-core/log/param_formatter.rb +7 -3
  39. data/lib/aws-sdk-core/log.rb +10 -0
  40. data/lib/aws-sdk-core/login_credentials.rb +229 -0
  41. data/lib/aws-sdk-core/lru_cache.rb +75 -0
  42. data/lib/aws-sdk-core/pageable_response.rb +1 -1
  43. data/lib/aws-sdk-core/param_validator.rb +7 -2
  44. data/lib/aws-sdk-core/plugins/bearer_authorization.rb +2 -0
  45. data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +347 -170
  46. data/lib/aws-sdk-core/plugins/client_metrics_plugin.rb +1 -1
  47. data/lib/aws-sdk-core/plugins/client_metrics_send_plugin.rb +14 -2
  48. data/lib/aws-sdk-core/plugins/credentials_configuration.rb +78 -56
  49. data/lib/aws-sdk-core/plugins/endpoint_pattern.rb +40 -32
  50. data/lib/aws-sdk-core/plugins/global_configuration.rb +8 -9
  51. data/lib/aws-sdk-core/plugins/http_checksum.rb +2 -8
  52. data/lib/aws-sdk-core/plugins/invocation_id.rb +1 -11
  53. data/lib/aws-sdk-core/plugins/logging.rb +2 -0
  54. data/lib/aws-sdk-core/plugins/protocols/api_gateway.rb +3 -1
  55. data/lib/aws-sdk-core/plugins/protocols/ec2.rb +2 -24
  56. data/lib/aws-sdk-core/plugins/protocols/json_rpc.rb +6 -8
  57. data/lib/aws-sdk-core/plugins/protocols/query.rb +4 -2
  58. data/lib/aws-sdk-core/plugins/protocols/rest_json.rb +3 -15
  59. data/lib/aws-sdk-core/plugins/protocols/rest_xml.rb +3 -0
  60. data/lib/aws-sdk-core/plugins/protocols/rpc_v2.rb +17 -0
  61. data/lib/aws-sdk-core/plugins/regional_endpoint.rb +74 -25
  62. data/lib/aws-sdk-core/plugins/request_compression.rb +11 -2
  63. data/lib/aws-sdk-core/plugins/retry_errors.rb +12 -3
  64. data/lib/aws-sdk-core/plugins/sign.rb +42 -26
  65. data/lib/aws-sdk-core/plugins/signature_v2.rb +2 -1
  66. data/lib/aws-sdk-core/plugins/signature_v4.rb +2 -1
  67. data/lib/aws-sdk-core/plugins/stub_responses.rb +59 -9
  68. data/lib/aws-sdk-core/plugins/telemetry.rb +75 -0
  69. data/lib/aws-sdk-core/plugins/transfer_encoding.rb +16 -9
  70. data/lib/aws-sdk-core/plugins/user_agent.rb +103 -26
  71. data/lib/aws-sdk-core/plugins.rb +39 -0
  72. data/lib/aws-sdk-core/process_credentials.rb +48 -29
  73. data/lib/aws-sdk-core/query/ec2_handler.rb +27 -0
  74. data/lib/aws-sdk-core/query/ec2_param_builder.rb +5 -7
  75. data/lib/aws-sdk-core/query/handler.rb +4 -4
  76. data/lib/aws-sdk-core/query/param_builder.rb +2 -2
  77. data/lib/aws-sdk-core/query.rb +2 -1
  78. data/lib/aws-sdk-core/refreshing_credentials.rb +8 -11
  79. data/lib/aws-sdk-core/resources.rb +8 -0
  80. data/lib/aws-sdk-core/rest/content_type_handler.rb +60 -0
  81. data/lib/aws-sdk-core/rest/handler.rb +3 -4
  82. data/lib/aws-sdk-core/rest/request/body.rb +32 -5
  83. data/lib/aws-sdk-core/rest/request/endpoint.rb +24 -4
  84. data/lib/aws-sdk-core/rest/request/headers.rb +15 -7
  85. data/lib/aws-sdk-core/rest/request/querystring_builder.rb +23 -11
  86. data/lib/aws-sdk-core/rest/response/body.rb +15 -1
  87. data/lib/aws-sdk-core/rest/response/header_list_parser.rb +79 -0
  88. data/lib/aws-sdk-core/rest/response/headers.rb +8 -3
  89. data/lib/aws-sdk-core/rest.rb +1 -0
  90. data/lib/aws-sdk-core/rpc_v2/builder.rb +62 -0
  91. data/lib/aws-sdk-core/rpc_v2/cbor_engine.rb +18 -0
  92. data/lib/aws-sdk-core/rpc_v2/content_type_handler.rb +47 -0
  93. data/lib/aws-sdk-core/rpc_v2/error_handler.rb +95 -0
  94. data/lib/aws-sdk-core/rpc_v2/handler.rb +79 -0
  95. data/lib/aws-sdk-core/rpc_v2/parser.rb +98 -0
  96. data/lib/aws-sdk-core/rpc_v2.rb +69 -0
  97. data/lib/aws-sdk-core/shared_config.rb +108 -22
  98. data/lib/aws-sdk-core/shared_credentials.rb +1 -7
  99. data/lib/aws-sdk-core/sso_credentials.rb +5 -2
  100. data/lib/aws-sdk-core/static_token_provider.rb +1 -2
  101. data/lib/aws-sdk-core/stubbing/protocols/ec2.rb +12 -11
  102. data/lib/aws-sdk-core/stubbing/protocols/json.rb +11 -10
  103. data/lib/aws-sdk-core/stubbing/protocols/query.rb +7 -6
  104. data/lib/aws-sdk-core/stubbing/protocols/rest.rb +2 -1
  105. data/lib/aws-sdk-core/stubbing/protocols/rest_json.rb +9 -8
  106. data/lib/aws-sdk-core/stubbing/protocols/rest_xml.rb +6 -5
  107. data/lib/aws-sdk-core/stubbing/protocols/rpc_v2.rb +39 -0
  108. data/lib/aws-sdk-core/stubbing.rb +22 -0
  109. data/lib/aws-sdk-core/telemetry/base.rb +177 -0
  110. data/lib/aws-sdk-core/telemetry/no_op.rb +70 -0
  111. data/lib/aws-sdk-core/telemetry/otel.rb +235 -0
  112. data/lib/aws-sdk-core/telemetry/span_kind.rb +22 -0
  113. data/lib/aws-sdk-core/telemetry/span_status.rb +59 -0
  114. data/lib/aws-sdk-core/telemetry.rb +78 -0
  115. data/lib/aws-sdk-core/token.rb +3 -3
  116. data/lib/aws-sdk-core/token_provider.rb +4 -0
  117. data/lib/aws-sdk-core/token_provider_chain.rb +2 -6
  118. data/lib/aws-sdk-core/util.rb +41 -1
  119. data/lib/aws-sdk-core/waiters/poller.rb +10 -5
  120. data/lib/aws-sdk-core/xml/builder.rb +17 -9
  121. data/lib/aws-sdk-core/xml/error_handler.rb +35 -43
  122. data/lib/aws-sdk-core/xml/parser/frame.rb +4 -20
  123. data/lib/aws-sdk-core/xml/parser/stack.rb +2 -0
  124. data/lib/aws-sdk-core/xml/parser.rb +2 -6
  125. data/lib/aws-sdk-core.rb +86 -107
  126. data/lib/aws-sdk-signin/client.rb +604 -0
  127. data/lib/aws-sdk-signin/client_api.rb +119 -0
  128. data/lib/aws-sdk-signin/customizations.rb +1 -0
  129. data/lib/aws-sdk-signin/endpoint_parameters.rb +69 -0
  130. data/lib/aws-sdk-signin/endpoint_provider.rb +59 -0
  131. data/lib/aws-sdk-signin/endpoints.rb +20 -0
  132. data/lib/aws-sdk-signin/errors.rb +122 -0
  133. data/lib/aws-sdk-signin/plugins/endpoints.rb +77 -0
  134. data/lib/aws-sdk-signin/resource.rb +26 -0
  135. data/lib/aws-sdk-signin/types.rb +299 -0
  136. data/lib/aws-sdk-signin.rb +63 -0
  137. data/lib/aws-sdk-sso/client.rb +189 -96
  138. data/lib/aws-sdk-sso/client_api.rb +7 -0
  139. data/lib/aws-sdk-sso/endpoint_parameters.rb +13 -10
  140. data/lib/aws-sdk-sso/endpoint_provider.rb +16 -20
  141. data/lib/aws-sdk-sso/endpoints.rb +2 -54
  142. data/lib/aws-sdk-sso/plugins/endpoints.rb +20 -20
  143. data/lib/aws-sdk-sso/types.rb +1 -0
  144. data/lib/aws-sdk-sso.rb +15 -11
  145. data/lib/aws-sdk-ssooidc/client.rb +293 -122
  146. data/lib/aws-sdk-ssooidc/client_api.rb +38 -0
  147. data/lib/aws-sdk-ssooidc/endpoint_parameters.rb +13 -10
  148. data/lib/aws-sdk-ssooidc/endpoint_provider.rb +14 -18
  149. data/lib/aws-sdk-ssooidc/endpoints.rb +2 -54
  150. data/lib/aws-sdk-ssooidc/errors.rb +31 -0
  151. data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +20 -20
  152. data/lib/aws-sdk-ssooidc/types.rb +142 -29
  153. data/lib/aws-sdk-ssooidc.rb +15 -11
  154. data/lib/aws-sdk-sts/client.rb +529 -156
  155. data/lib/aws-sdk-sts/client_api.rb +108 -8
  156. data/lib/aws-sdk-sts/customizations.rb +5 -2
  157. data/lib/aws-sdk-sts/endpoint_parameters.rb +15 -14
  158. data/lib/aws-sdk-sts/endpoint_provider.rb +50 -55
  159. data/lib/aws-sdk-sts/endpoints.rb +2 -118
  160. data/lib/aws-sdk-sts/errors.rb +79 -0
  161. data/lib/aws-sdk-sts/plugins/endpoints.rb +20 -28
  162. data/lib/aws-sdk-sts/presigner.rb +2 -6
  163. data/lib/aws-sdk-sts/types.rb +344 -32
  164. data/lib/aws-sdk-sts.rb +15 -11
  165. data/lib/seahorse/client/async_base.rb +4 -5
  166. data/lib/seahorse/client/async_response.rb +19 -0
  167. data/lib/seahorse/client/base.rb +18 -21
  168. data/lib/seahorse/client/h2/connection.rb +18 -28
  169. data/lib/seahorse/client/h2/handler.rb +19 -3
  170. data/lib/seahorse/client/handler.rb +1 -1
  171. data/lib/seahorse/client/http/response.rb +1 -1
  172. data/lib/seahorse/client/net_http/connection_pool.rb +15 -12
  173. data/lib/seahorse/client/net_http/handler.rb +21 -9
  174. data/lib/seahorse/client/networking_error.rb +1 -1
  175. data/lib/seahorse/client/plugin.rb +9 -0
  176. data/lib/seahorse/client/plugins/endpoint.rb +0 -1
  177. data/lib/seahorse/client/plugins/h2.rb +4 -4
  178. data/lib/seahorse/client/plugins/net_http.rb +57 -16
  179. data/lib/seahorse/client/request_context.rb +9 -2
  180. data/lib/seahorse/client/response.rb +2 -0
  181. data/lib/seahorse/model/shapes.rb +2 -2
  182. data/lib/seahorse/util.rb +2 -1
  183. data/sig/aws-sdk-core/async_client_stubs.rbs +21 -0
  184. data/sig/aws-sdk-core/client_stubs.rbs +10 -0
  185. data/sig/aws-sdk-core/errors.rbs +22 -0
  186. data/sig/aws-sdk-core/resources/collection.rbs +21 -0
  187. data/sig/aws-sdk-core/structure.rbs +4 -0
  188. data/sig/aws-sdk-core/telemetry/base.rbs +46 -0
  189. data/sig/aws-sdk-core/telemetry/otel.rbs +22 -0
  190. data/sig/aws-sdk-core/telemetry/span_kind.rbs +15 -0
  191. data/sig/aws-sdk-core/telemetry/span_status.rbs +24 -0
  192. data/sig/aws-sdk-core/waiters/errors.rbs +20 -0
  193. data/sig/aws-sdk-core.rbs +7 -0
  194. data/sig/seahorse/client/async_base.rbs +18 -0
  195. data/sig/seahorse/client/base.rbs +25 -0
  196. data/sig/seahorse/client/handler_builder.rbs +16 -0
  197. data/sig/seahorse/client/response.rbs +61 -0
  198. metadata +117 -23
  199. /data/lib/aws-sdk-core/xml/parser/{engines/libxml.rb → libxml_engine.rb} +0 -0
  200. /data/lib/aws-sdk-core/xml/parser/{engines/nokogiri.rb → nokogiri_engine.rb} +0 -0
  201. /data/lib/aws-sdk-core/xml/parser/{engines/oga.rb → oga_engine.rb} +0 -0
  202. /data/lib/aws-sdk-core/xml/parser/{engines/ox.rb → ox_engine.rb} +0 -0
  203. /data/lib/aws-sdk-core/xml/parser/{engines/rexml.rb → rexml_engine.rb} +0 -0
@@ -17,6 +17,8 @@ module Aws
17
17
 
18
18
  # @param [String<JSON>] json
19
19
  def parse(json, target = nil)
20
+ json = '{}' if json.empty?
21
+
20
22
  parse_ref(@rules, Json.load(json), target)
21
23
  end
22
24
 
@@ -69,6 +71,8 @@ module Aws
69
71
  def map(ref, values, target = nil)
70
72
  target = {} if target.nil?
71
73
  values.each do |key, value|
74
+ next if value.nil?
75
+
72
76
  target[key] = parse_ref(ref.shape.value, value)
73
77
  end
74
78
  target
@@ -85,6 +89,7 @@ module Aws
85
89
  when TimestampShape then time(value)
86
90
  when BlobShape then Base64.decode64(value)
87
91
  when BooleanShape then value.to_s == 'true'
92
+ when FloatShape then Util.deserialize_number(value)
88
93
  else value
89
94
  end
90
95
  end
@@ -93,7 +98,7 @@ module Aws
93
98
  # @param [String, Integer] value
94
99
  # @return [Time]
95
100
  def time(value)
96
- value.is_a?(Numeric) ? Time.at(value) : Time.parse(value)
101
+ value.is_a?(Numeric) ? Time.at(value) : Aws::Util.deserialize_time(value)
97
102
  end
98
103
 
99
104
  def flattened_list?(shape)
@@ -1,12 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
3
  require_relative 'json/builder'
5
4
  require_relative 'json/error_handler'
6
5
  require_relative 'json/handler'
7
6
  require_relative 'json/parser'
8
- require_relative 'json/json_engine'
9
- require_relative 'json/oj_engine'
10
7
 
11
8
  module Aws
12
9
  # @api private
@@ -21,29 +18,61 @@ module Aws
21
18
  end
22
19
 
23
20
  class << self
24
- def load(json)
25
- ENGINE.load(json)
21
+ # @param [Symbol,Class] engine
22
+ # Must be one of the following values:
23
+ #
24
+ # * :oj
25
+ # * :json
26
+ #
27
+ def engine=(engine)
28
+ @engine = Class === engine ? engine : load_engine(engine)
29
+ end
30
+
31
+ # @return [Class] Returns the default engine.
32
+ # One of:
33
+ #
34
+ # * {OjEngine}
35
+ # * {JsonEngine}
36
+ #
37
+ def engine
38
+ set_default_engine unless @engine
39
+ @engine
26
40
  end
27
41
 
28
- def load_file(path)
29
- load(File.open(path, 'r', encoding: 'UTF-8', &:read))
42
+ def load(json)
43
+ @engine.load(json)
30
44
  end
31
45
 
32
46
  def dump(value)
33
- ENGINE.dump(value)
47
+ @engine.dump(value)
48
+ end
49
+
50
+ def set_default_engine
51
+ [:oj, :json].each do |name|
52
+ @engine ||= try_load_engine(name)
53
+ end
54
+ unless @engine
55
+ raise 'Unable to find a compatible json library. ' \
56
+ 'Ensure that you have installed or added to your Gemfile one of ' \
57
+ 'oj or json'
58
+ end
34
59
  end
35
60
 
36
61
  private
37
62
 
38
- def select_engine
39
- require 'oj'
40
- OjEngine
63
+ def load_engine(name)
64
+ require "aws-sdk-core/json/#{name}_engine"
65
+ const_name = name[0].upcase + name[1..-1] + 'Engine'
66
+ const_get(const_name)
67
+ end
68
+
69
+ def try_load_engine(name)
70
+ load_engine(name)
41
71
  rescue LoadError
42
- JSONEngine
72
+ false
43
73
  end
44
74
  end
45
75
 
46
- # @api private
47
- ENGINE = select_engine
76
+ set_default_engine
48
77
  end
49
78
  end
@@ -55,14 +55,14 @@ module Aws
55
55
  filtered[key] = if @enabled && filters.include?(key)
56
56
  '[FILTERED]'
57
57
  else
58
- filter(value, type)
58
+ filter(value, value.class)
59
59
  end
60
60
  end
61
61
  filtered
62
62
  end
63
63
 
64
64
  def filter_array(values, type)
65
- values.map { |value| filter(value, type) }
65
+ values.map { |value| filter(value, value.class) }
66
66
  end
67
67
 
68
68
  end
@@ -51,13 +51,17 @@ module Aws
51
51
  when String then summarize_string(value)
52
52
  when Hash then '{' + summarize_hash(value) + '}'
53
53
  when Array then summarize_array(value)
54
- when File then summarize_file(value.path)
55
- when Pathname then summarize_file(value)
54
+ when File then summarize_file(value)
55
+ when Pathname then summarize_filepath(value)
56
56
  else value.inspect
57
57
  end
58
58
  end
59
59
 
60
- def summarize_file(path)
60
+ def summarize_file(file)
61
+ "#<File:#{file.path} (#{file.size} bytes)>"
62
+ end
63
+
64
+ def summarize_filepath(path)
61
65
  "#<File:#{path} (#{File.size(path)} bytes)>"
62
66
  end
63
67
 
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ # setup autoloading for Log module
5
+ module Log
6
+ autoload :Formatter, 'aws-sdk-core/log/formatter'
7
+ autoload :ParamFilter, 'aws-sdk-core/log/param_filter'
8
+ autoload :ParamFormatter, 'aws-sdk-core/log/param_formatter'
9
+ end
10
+ end
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ # An auto-refreshing credential provider that retrieves credentials from
5
+ # a cached login token. This class does NOT implement the AWS Sign-In
6
+ # login flow - tokens must be generated separately by running `aws login`
7
+ # from the AWS CLI/AWS Tools for PowerShell with the correct profile.
8
+ # The {LoginCredentials} will auto-refresh the AWS credentials from AWS Sign-In.
9
+ #
10
+ # # You must first run aws login --profile your-login-profile
11
+ # login_credentials = Aws::LoginCredentials.new(login_session: 'my_login_session')
12
+ # ec2 = Aws::EC2::Client.new(credentials: login_credentials)
13
+ #
14
+ # If you omit the `:client` option, a new {Aws::Signin::Client} object will
15
+ # be constructed with additional options that were provided.
16
+ class LoginCredentials
17
+ include CredentialProvider
18
+ include RefreshingCredentials
19
+
20
+ # @option options [required, String] :login_session An opaque string
21
+ # used to determine the cache file location. This value can be found
22
+ # in the AWS config file which is set by the AWS CLI/AWS Tools for
23
+ # PowerShell automatically.
24
+ #
25
+ # @option options [Signin::Client] :client Optional `Signin::Client`.
26
+ # If not provided, a client will be constructed.
27
+ def initialize(options = {})
28
+ raise ArgumentError, 'Missing login_session' unless options[:login_session]
29
+
30
+ @login_session = options.delete(:login_session)
31
+ @client = options[:client]
32
+ unless @client
33
+ client_opts = options.reject { |key, _| CLIENT_EXCLUDE_OPTIONS.include?(key) }
34
+ @client = Signin::Client.new(client_opts.merge(credentials: nil))
35
+ end
36
+ @metrics = ['CREDENTIALS_LOGIN']
37
+ @async_refresh = true
38
+ super
39
+ end
40
+
41
+ # @return [Signin::Client]
42
+ attr_reader :client
43
+
44
+ private
45
+
46
+ def refresh
47
+ # First reload the token from disk to ensure it hasn't been refreshed externally
48
+ token_json = read_cached_token
49
+ update_creds(token_json['accessToken'])
50
+ return if @credentials && @expiration && !near_expiration?(sync_expiration_length)
51
+
52
+ # Using OpenSSL 3.6.0 may result in errors like "certificate verify failed (unable to get certificate CRL)."
53
+ # A recommended workaround is to use OpenSSL version < 3.6.0 or requiring the openssl gem with a version of at
54
+ # least 3.2.2. GitHub issue: https://github.com/openssl/openssl/issues/28752.
55
+ if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('3.6.') &&
56
+ (!Gem.loaded_specs['openssl'] || Gem.loaded_specs['openssl'].version < Gem::Version.new('3.2.2'))
57
+ warn 'WARNING: OpenSSL 3.6.x may cause certificate verify errors - use OpenSSL < 3.6.0 or openssl gem >= 3.2.2'
58
+ end
59
+
60
+ # Attempt to refresh the token
61
+ attempt_refresh(token_json)
62
+
63
+ # Raise if token is hard expired
64
+ return unless !@expiration || @expiration < Time.now
65
+
66
+ raise Errors::InvalidLoginToken,
67
+ 'Login token is invalid and failed to refresh. Please reauthenticate.'
68
+ end
69
+
70
+ def read_cached_token
71
+ cached_token = JSON.load_file(login_cache_file)
72
+ validate_cached_token(cached_token)
73
+ cached_token
74
+ rescue Errno::ENOENT, Aws::Json::ParseError
75
+ raise Errors::InvalidLoginToken,
76
+ "Failed to load a Login token for login session #{@login_session}. Please reauthenticate."
77
+ end
78
+
79
+ def login_cache_file
80
+ directory = ENV['AWS_LOGIN_CACHE_DIRECTORY'] || File.join(Dir.home, '.aws', 'login', 'cache')
81
+ login_session_sha = OpenSSL::Digest::SHA256.hexdigest(@login_session.strip.encode('utf-8'))
82
+ File.join(directory, "#{login_session_sha}.json")
83
+ end
84
+
85
+ def validate_cached_token(cached_token)
86
+ required_cached_token_fields = %w[accessToken clientId refreshToken dpopKey]
87
+ missing_fields = required_cached_token_fields.reject { |field| cached_token[field] }
88
+ unless missing_fields.empty?
89
+ raise ArgumentError, "Cached login token is missing required field(s): #{missing_fields}. " \
90
+ 'Please reauthenticate.'
91
+ end
92
+
93
+ access_token = cached_token['accessToken']
94
+ required_access_token_fields = %w[accessKeyId secretAccessKey sessionToken accountId expiresAt]
95
+ missing_fields = required_access_token_fields.reject { |field| access_token[field] }
96
+
97
+ return if missing_fields.empty?
98
+
99
+ raise ArgumentError, "Access token in cached login token is missing required field(s): #{missing_fields}. " \
100
+ 'Please reauthenticate.'
101
+ end
102
+
103
+ def update_creds(access_token)
104
+ @credentials = Credentials.new(
105
+ access_token['accessKeyId'],
106
+ access_token['secretAccessKey'],
107
+ access_token['sessionToken'],
108
+ account_id: access_token['accountId']
109
+ )
110
+ @expiration = Time.parse(access_token['expiresAt'])
111
+ end
112
+
113
+ def attempt_refresh(token_json)
114
+ resp = make_request(token_json)
115
+ parse_resp(resp.token_output, token_json)
116
+ update_creds(token_json['accessToken'])
117
+ update_token_cache(token_json)
118
+ rescue Signin::Errors::AccessDeniedException => e
119
+ case e.error
120
+ when 'TOKEN_EXPIRED'
121
+ warn 'Your session has expired. Please reauthenticate.'
122
+ when 'USER_CREDENTIALS_CHANGED'
123
+ warn 'Unable to refresh credentials because of a change in your password. ' \
124
+ 'Please reauthenticate with your new password.'
125
+ when 'INSUFFICIENT_PERMISSIONS'
126
+ warn 'Unable to refresh credentials due to insufficient permissions. ' \
127
+ 'You may be missing permission for the `CreateOAuth2Token` action.'
128
+ end
129
+ rescue StandardError => e
130
+ warn("Failed to refresh Login token for LoginCredentials: #{e.message}")
131
+ end
132
+
133
+ def make_request(token_json)
134
+ options = {
135
+ token_input: {
136
+ client_id: token_json['clientId'],
137
+ grant_type: 'refresh_token',
138
+ refresh_token: token_json['refreshToken']
139
+ }
140
+ }
141
+ req = @client.build_request(:create_o_auth_2_token, options)
142
+ endpoint_params = Aws::Signin::EndpointParameters.create(req.context.config)
143
+ endpoint = req.context.config.endpoint_provider.resolve_endpoint(endpoint_params)
144
+ endpoint = URI.join(endpoint.url, @client.config.api.operation(:create_o_auth_2_token).http_request_uri).to_s
145
+ req.context.http_request.headers['DPoP'] = dpop_proof(token_json['dpopKey'], endpoint)
146
+ req.send_request
147
+ end
148
+
149
+ def dpop_proof(dpop_key, endpoint)
150
+ # Load private key from cached token file
151
+ private_key = OpenSSL::PKey.read(dpop_key)
152
+ public_key = private_key.public_key.to_octet_string(:uncompressed)
153
+
154
+ # Construct header and payload
155
+ header = build_header(public_key[1, 32], public_key[33, 32])
156
+ payload = build_payload(endpoint)
157
+
158
+ # Base64URL encode header and payload, sign message using private key, and create header
159
+ message = build_message(header, payload)
160
+ signature = private_key.sign(OpenSSL::Digest.new('SHA256'), message)
161
+ jws_signature = der_to_jws(signature)
162
+ "#{message}.#{Base64.urlsafe_encode64(jws_signature, padding: false)}"
163
+ end
164
+
165
+ def build_header(x_bytes, y_bytes)
166
+ {
167
+ 'alg' => 'ES256', # signing algorithm
168
+ 'jwk' => {
169
+ 'crv' => 'P-256', # curve name
170
+ 'kty' => 'EC', # key type
171
+ 'x' => Base64.urlsafe_encode64(x_bytes, padding: false), # public x coordinate
172
+ 'y' => Base64.urlsafe_encode64(y_bytes, padding: false) # public y coordinate
173
+ },
174
+ 'typ' => 'dpop+jwt' # hardcoded
175
+ }
176
+ end
177
+
178
+ def build_payload(htu)
179
+ {
180
+ 'jti' => SecureRandom.uuid, # unique identifier (UUID4)
181
+ 'htm' => @client.config.api.operation(:create_o_auth_2_token).http_method, # POST
182
+ 'htu' => htu, # endpoint of the CreateOAuth2Token operation, with path
183
+ 'iat' => Time.now.utc.to_i # UTC timestamp, specified number of seconds from 1970-01-01T00:00:00Z UTC
184
+ }
185
+ end
186
+
187
+ def build_message(header, payload)
188
+ encoded_header = Base64.urlsafe_encode64(JSON.dump(header), padding: false)
189
+ encoded_payload = Base64.urlsafe_encode64(JSON.dump(payload), padding: false)
190
+ "#{encoded_header}.#{encoded_payload}"
191
+ end
192
+
193
+ # Converts DER-encoded ASN.1 signature to JWS
194
+ def der_to_jws(der_signature)
195
+ asn1 = OpenSSL::ASN1.decode(der_signature)
196
+ r = asn1.value[0].value
197
+ s = asn1.value[1].value
198
+
199
+ r_hex = r.to_s(16).rjust(64, '0')
200
+ s_hex = s.to_s(16).rjust(64, '0')
201
+
202
+ [r_hex + s_hex].pack('H*')
203
+ end
204
+
205
+ def parse_resp(resp, token_json)
206
+ access_token = token_json['accessToken']
207
+ access_token.merge!(
208
+ 'accessKeyId' => resp.access_token.access_key_id,
209
+ 'secretAccessKey' => resp.access_token.secret_access_key,
210
+ 'sessionToken' => resp.access_token.session_token,
211
+ 'expiresAt' => (Time.now.utc + resp.expires_in).to_datetime.rfc3339
212
+ )
213
+ token_json['refreshToken'] = resp.refresh_token
214
+ end
215
+
216
+ def update_token_cache(token_json)
217
+ cached_token = token_json.dup
218
+ # File.write is not atomic so use temp file and move
219
+ temp_file = Tempfile.new('temp_file')
220
+ begin
221
+ temp_file.write(Json.dump(cached_token))
222
+ temp_file.close
223
+ FileUtils.mv(temp_file.path, login_cache_file)
224
+ ensure
225
+ temp_file.unlink if File.exist?(temp_file.path) # Ensure temp file is cleaned up
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ # @api private
5
+ # A simple thread safe LRU cache
6
+ class LRUCache
7
+ # @param [Hash] options
8
+ # @option options [Integer] :max_entries (100) Maximum number of entries
9
+ # @option options [Integer] :expiration (nil) Expiration time in seconds
10
+ def initialize(options = {})
11
+ @max_entries = options[:max_entries] || 100
12
+ @expiration = options[:expiration]
13
+ @entries = {}
14
+ @mutex = Mutex.new
15
+ end
16
+
17
+ # @param [String] key
18
+ # @return [Object]
19
+ def [](key)
20
+ @mutex.synchronize do
21
+ value = @entries[key]
22
+ if value
23
+ @entries.delete(key)
24
+ @entries[key] = value unless value.expired?
25
+ end
26
+ @entries[key]&.value
27
+ end
28
+ end
29
+
30
+ # @param [String] key
31
+ # @param [Object] value
32
+ def []=(key, value)
33
+ @mutex.synchronize do
34
+ @entries.shift unless @entries.size < @max_entries
35
+ # delete old value if exists
36
+ @entries.delete(key)
37
+ @entries[key] = Entry.new(value: value, expiration: @expiration)
38
+ @entries[key].value
39
+ end
40
+ end
41
+
42
+ # @param [String] key
43
+ # @return [Boolean]
44
+ def key?(key)
45
+ @mutex.synchronize do
46
+ @entries.delete(key) if @entries.key?(key) && @entries[key].expired?
47
+ @entries.key?(key)
48
+ end
49
+ end
50
+
51
+ def clear
52
+ @mutex.synchronize do
53
+ @entries.clear
54
+ end
55
+ end
56
+
57
+ # @api private
58
+ class Entry
59
+ def initialize(options = {})
60
+ @value = options[:value]
61
+ @expiration = options[:expiration]
62
+ @created_time = Time.now
63
+ end
64
+
65
+ # @return [Object]
66
+ attr_reader :value
67
+
68
+ def expired?
69
+ return false unless @expiration
70
+
71
+ Time.now - @created_time > @expiration
72
+ end
73
+ end
74
+ end
75
+ end
@@ -201,7 +201,7 @@ module Aws
201
201
  def next_response(params)
202
202
  params = next_page_params(params)
203
203
  request = context.client.build_request(context.operation_name, params)
204
- Aws::Plugins::UserAgent.feature('paginator') do
204
+ Aws::Plugins::UserAgent.metric('PAGINATOR') do
205
205
  request.send_request
206
206
  end
207
207
  end
@@ -71,9 +71,10 @@ module Aws
71
71
  end
72
72
 
73
73
  if @validate_required && shape.union
74
- if values.length > 1
74
+ set_values = values.to_h.length
75
+ if set_values > 1
75
76
  errors << "multiple values provided to union at #{context} - must contain exactly one of the supported types: #{shape.member_names.join(', ')}"
76
- elsif values.length == 0
77
+ elsif set_values == 0
77
78
  errors << "No values provided to union at #{context} - must contain exactly one of the supported types: #{shape.member_names.join(', ')}"
78
79
  end
79
80
  end
@@ -106,6 +107,8 @@ module Aws
106
107
  # validate members
107
108
  member_ref = ref.shape.member
108
109
  values.each.with_index do |value, index|
110
+ next unless value
111
+
109
112
  shape(member_ref, value, errors, context + "[#{index}]")
110
113
  end
111
114
  end
@@ -121,6 +124,8 @@ module Aws
121
124
 
122
125
  values.each do |key, value|
123
126
  shape(key_ref, key, errors, "#{context} #{key.inspect} key")
127
+ next unless value
128
+
124
129
  shape(value_ref, value, errors, context + "[#{key.inspect}]")
125
130
  end
126
131
  end
@@ -4,6 +4,8 @@ module Aws
4
4
  # @api private
5
5
  module Plugins
6
6
  # @api private
7
+ # Deprecated - does not look at new traits like `auth` and `unsignedPayload`
8
+ # Necessary to exist after endpoints 2.0 for old service clients + new core
7
9
  class BearerAuthorization < Seahorse::Client::Plugin
8
10
 
9
11
  option(:token_provider,