aws-sdk-core 3.131.1 → 3.168.1

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +244 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-sdk-core/arn.rb +13 -0
  5. data/lib/aws-sdk-core/binary/encode_handler.rb +12 -1
  6. data/lib/aws-sdk-core/endpoints/condition.rb +36 -0
  7. data/lib/aws-sdk-core/endpoints/endpoint.rb +17 -0
  8. data/lib/aws-sdk-core/endpoints/endpoint_rule.rb +71 -0
  9. data/lib/aws-sdk-core/endpoints/error_rule.rb +37 -0
  10. data/lib/aws-sdk-core/endpoints/function.rb +75 -0
  11. data/lib/aws-sdk-core/endpoints/matchers.rb +127 -0
  12. data/lib/aws-sdk-core/endpoints/reference.rb +26 -0
  13. data/lib/aws-sdk-core/endpoints/rule.rb +20 -0
  14. data/lib/aws-sdk-core/endpoints/rule_set.rb +47 -0
  15. data/lib/aws-sdk-core/endpoints/rules_provider.rb +32 -0
  16. data/lib/aws-sdk-core/endpoints/templater.rb +52 -0
  17. data/lib/aws-sdk-core/endpoints/tree_rule.rb +40 -0
  18. data/lib/aws-sdk-core/endpoints/url.rb +59 -0
  19. data/lib/aws-sdk-core/endpoints.rb +74 -0
  20. data/lib/aws-sdk-core/errors.rb +13 -0
  21. data/lib/aws-sdk-core/json/error_handler.rb +10 -1
  22. data/lib/aws-sdk-core/pageable_response.rb +7 -0
  23. data/lib/aws-sdk-core/plugins/bearer_authorization.rb +67 -0
  24. data/lib/aws-sdk-core/plugins/credentials_configuration.rb +24 -0
  25. data/lib/aws-sdk-core/plugins/endpoint_discovery.rb +6 -2
  26. data/lib/aws-sdk-core/plugins/jsonvalue_converter.rb +34 -6
  27. data/lib/aws-sdk-core/plugins/recursion_detection.rb +14 -3
  28. data/lib/aws-sdk-core/plugins/regional_endpoint.rb +5 -0
  29. data/lib/aws-sdk-core/plugins/sign.rb +190 -0
  30. data/lib/aws-sdk-core/plugins/signature_v2.rb +1 -0
  31. data/lib/aws-sdk-core/plugins/signature_v4.rb +13 -7
  32. data/lib/aws-sdk-core/refreshing_token.rb +71 -0
  33. data/lib/aws-sdk-core/rest/handler.rb +1 -1
  34. data/lib/aws-sdk-core/rest/request/headers.rb +2 -6
  35. data/lib/aws-sdk-core/shared_config.rb +76 -5
  36. data/lib/aws-sdk-core/sso_credentials.rb +79 -44
  37. data/lib/aws-sdk-core/sso_token_provider.rb +135 -0
  38. data/lib/aws-sdk-core/static_token_provider.rb +14 -0
  39. data/lib/aws-sdk-core/structure.rb +6 -4
  40. data/lib/aws-sdk-core/token.rb +31 -0
  41. data/lib/aws-sdk-core/token_provider.rb +15 -0
  42. data/lib/aws-sdk-core/token_provider_chain.rb +51 -0
  43. data/lib/aws-sdk-core/xml/error_handler.rb +7 -0
  44. data/lib/aws-sdk-core.rb +14 -0
  45. data/lib/aws-sdk-sso/client.rb +51 -11
  46. data/lib/aws-sdk-sso/endpoint_parameters.rb +66 -0
  47. data/lib/aws-sdk-sso/endpoint_provider.rb +112 -0
  48. data/lib/aws-sdk-sso/endpoints.rb +71 -0
  49. data/lib/aws-sdk-sso/plugins/endpoints.rb +76 -0
  50. data/lib/aws-sdk-sso/types.rb +8 -8
  51. data/lib/aws-sdk-sso.rb +5 -1
  52. data/lib/aws-sdk-ssooidc/client.rb +606 -0
  53. data/lib/aws-sdk-ssooidc/client_api.rb +216 -0
  54. data/lib/aws-sdk-ssooidc/customizations.rb +1 -0
  55. data/lib/aws-sdk-ssooidc/endpoint_parameters.rb +66 -0
  56. data/lib/aws-sdk-ssooidc/endpoint_provider.rb +111 -0
  57. data/lib/aws-sdk-ssooidc/endpoints.rb +57 -0
  58. data/lib/aws-sdk-ssooidc/errors.rb +290 -0
  59. data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +74 -0
  60. data/lib/aws-sdk-ssooidc/resource.rb +26 -0
  61. data/lib/aws-sdk-ssooidc/types.rb +534 -0
  62. data/lib/aws-sdk-ssooidc.rb +59 -0
  63. data/lib/aws-sdk-sts/client.rb +141 -124
  64. data/lib/aws-sdk-sts/endpoint_parameters.rb +78 -0
  65. data/lib/aws-sdk-sts/endpoint_provider.rb +229 -0
  66. data/lib/aws-sdk-sts/endpoints.rb +135 -0
  67. data/lib/aws-sdk-sts/plugins/endpoints.rb +84 -0
  68. data/lib/aws-sdk-sts/presigner.rb +13 -15
  69. data/lib/aws-sdk-sts/types.rb +79 -69
  70. data/lib/aws-sdk-sts.rb +5 -1
  71. data/lib/seahorse/client/async_base.rb +0 -1
  72. data/lib/seahorse/client/configuration.rb +2 -2
  73. data/lib/seahorse/client/h2/connection.rb +2 -5
  74. data/lib/seahorse/client/plugins/request_callback.rb +9 -9
  75. data/lib/seahorse/util.rb +4 -0
  76. metadata +47 -6
@@ -0,0 +1,190 @@
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
+ signer = Sign.signer_for(
41
+ context[:auth_scheme],
42
+ context.config,
43
+ context[:sigv4_region]
44
+ )
45
+
46
+ signer.sign(context)
47
+ @handler.call(context)
48
+ end
49
+ end
50
+
51
+ # @api private
52
+ class Bearer
53
+ def initialize
54
+ end
55
+
56
+ def sign(context)
57
+ if context.http_request.endpoint.scheme != 'https'
58
+ raise ArgumentError,
59
+ 'Unable to use bearer authorization on non https endpoint.'
60
+ end
61
+
62
+ token_provider = context.config.token_provider
63
+
64
+ raise Errors::MissingBearerTokenError unless token_provider&.set?
65
+
66
+ context.http_request.headers['Authorization'] =
67
+ "Bearer #{token_provider.token.token}"
68
+ end
69
+
70
+ def presign_url(*args)
71
+ raise ArgumentError, 'Bearer auth does not support presigned urls'
72
+ end
73
+
74
+ def sign_event(*args)
75
+ raise ArgumentError, 'Bearer auth does not support event signing'
76
+ end
77
+ end
78
+
79
+ # @api private
80
+ class SignatureV4
81
+ def initialize(auth_scheme, config, region_override = nil)
82
+ scheme_name = auth_scheme['name']
83
+
84
+ unless %w[sigv4 sigv4a].include?(scheme_name)
85
+ raise ArgumentError,
86
+ "Expected sigv4 or sigv4a auth scheme, got #{scheme_name}"
87
+ end
88
+
89
+ region = if scheme_name == 'sigv4a'
90
+ auth_scheme['signingRegionSet'].first
91
+ else
92
+ auth_scheme['signingRegion']
93
+ end
94
+ begin
95
+ @signer = Aws::Sigv4::Signer.new(
96
+ service: config.sigv4_name || auth_scheme['signingName'],
97
+ region: region_override || config.sigv4_region || region,
98
+ credentials_provider: config.credentials,
99
+ signing_algorithm: scheme_name.to_sym,
100
+ uri_escape_path: !!!auth_scheme['disableDoubleEncoding'],
101
+ unsigned_headers: %w[content-length user-agent x-amzn-trace-id]
102
+ )
103
+ rescue Aws::Sigv4::Errors::MissingCredentialsError
104
+ raise Aws::Errors::MissingCredentialsError
105
+ end
106
+ end
107
+
108
+ def sign(context)
109
+ req = context.http_request
110
+
111
+ apply_authtype(context, req)
112
+ reset_signature(req)
113
+ apply_clock_skew(context, req)
114
+
115
+ # compute the signature
116
+ begin
117
+ signature = @signer.sign_request(
118
+ http_method: req.http_method,
119
+ url: req.endpoint,
120
+ headers: req.headers,
121
+ body: req.body
122
+ )
123
+ rescue Aws::Sigv4::Errors::MissingCredentialsError
124
+ # Necessary for when credentials is explicitly set to nil
125
+ raise Aws::Errors::MissingCredentialsError
126
+ end
127
+ # apply signature headers
128
+ req.headers.update(signature.headers)
129
+
130
+ # add request metadata with signature components for debugging
131
+ context[:canonical_request] = signature.canonical_request
132
+ context[:string_to_sign] = signature.string_to_sign
133
+ end
134
+
135
+ def presign_url(*args)
136
+ @signer.presign_url(*args)
137
+ end
138
+
139
+ def sign_event(*args)
140
+ @signer.sign_event(*args)
141
+ end
142
+
143
+ private
144
+
145
+ def apply_authtype(context, req)
146
+ if context.operation['authtype'].eql?('v4-unsigned-body') &&
147
+ req.endpoint.scheme.eql?('https')
148
+ req.headers['X-Amz-Content-Sha256'] ||= 'UNSIGNED-PAYLOAD'
149
+ end
150
+ end
151
+
152
+ def reset_signature(req)
153
+ # in case this request is being re-signed
154
+ req.headers.delete('Authorization')
155
+ req.headers.delete('X-Amz-Security-Token')
156
+ req.headers.delete('X-Amz-Date')
157
+ req.headers.delete('x-Amz-Region-Set')
158
+ end
159
+
160
+ def apply_clock_skew(context, req)
161
+ if context.config.respond_to?(:clock_skew) &&
162
+ context.config.clock_skew &&
163
+ context.config.correct_clock_skew
164
+
165
+ endpoint = context.http_request.endpoint
166
+ skew = context.config.clock_skew.clock_correction(endpoint)
167
+ if skew.abs.positive?
168
+ req.headers['X-Amz-Date'] =
169
+ (Time.now.utc + skew).strftime('%Y%m%dT%H%M%SZ')
170
+ end
171
+ end
172
+ end
173
+
174
+ end
175
+
176
+ # @api private
177
+ class NullSigner
178
+
179
+ def sign(context)
180
+ end
181
+
182
+ def presign_url(*args)
183
+ end
184
+
185
+ def sign_event(*args)
186
+ end
187
+ end
188
+ end
189
+ end
190
+ 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
@@ -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
@@ -53,12 +53,8 @@ module Aws
53
53
  return if !value || value.empty?
54
54
  headers[ref.location_name] = value
55
55
  .compact
56
- .map { |s| escape_header_list_string(s.to_s) }
57
- .join(",")
58
- end
59
-
60
- def escape_header_list_string(s)
61
- (s.include?('"') || s.include?(",")) ? "\"#{s.gsub('"', '\"')}\"" : s
56
+ .map { |s| Seahorse::Util.escape_header_list_string(s.to_s) }
57
+ .join(',')
62
58
  end
63
59
 
64
60
  def apply_header_map(headers, ref, values)
@@ -3,7 +3,11 @@
3
3
  module Aws
4
4
  # @api private
5
5
  class SharedConfig
6
- SSO_PROFILE_KEYS = %w[sso_start_url sso_region sso_account_id sso_role_name].freeze
6
+ SSO_CREDENTIAL_PROFILE_KEYS = %w[sso_account_id sso_role_name].freeze
7
+ SSO_PROFILE_KEYS = %w[sso_session sso_start_url sso_region sso_account_id sso_role_name].freeze
8
+ SSO_TOKEN_PROFILE_KEYS = %w[sso_session].freeze
9
+ SSO_SESSION_KEYS = %w[sso_region sso_start_url].freeze
10
+
7
11
 
8
12
  # @return [String]
9
13
  attr_reader :credentials_path
@@ -51,10 +55,12 @@ module Aws
51
55
  @config_enabled = options[:config_enabled]
52
56
  @credentials_path = options[:credentials_path] ||
53
57
  determine_credentials_path
58
+ @credentials_path = File.expand_path(@credentials_path) if @credentials_path
54
59
  @parsed_credentials = {}
55
60
  load_credentials_file if loadable?(@credentials_path)
56
61
  if @config_enabled
57
62
  @config_path = options[:config_path] || determine_config_path
63
+ @config_path = File.expand_path(@config_path) if @config_path
58
64
  load_config_file if loadable?(@config_path)
59
65
  end
60
66
  end
@@ -149,6 +155,18 @@ module Aws
149
155
  credentials
150
156
  end
151
157
 
158
+ # Attempts to load from shared config or shared credentials file.
159
+ # Will always attempt first to load from the shared credentials
160
+ # file, if present.
161
+ def sso_token_from_config(opts = {})
162
+ p = opts[:profile] || @profile_name
163
+ token = sso_token_from_profile(@parsed_credentials, p)
164
+ if @parsed_config
165
+ token ||= sso_token_from_profile(@parsed_config, p)
166
+ end
167
+ token
168
+ end
169
+
152
170
  # Add an accessor method (similar to attr_reader) to return a configuration value
153
171
  # Uses the get_config_value below to control where
154
172
  # values are loaded from
@@ -314,13 +332,66 @@ module Aws
314
332
  def sso_credentials_from_profile(cfg, profile)
315
333
  if @parsed_config &&
316
334
  (prof_config = cfg[profile]) &&
317
- !(prof_config.keys & SSO_PROFILE_KEYS).empty?
335
+ !(prof_config.keys & SSO_CREDENTIAL_PROFILE_KEYS).empty?
336
+
337
+ if sso_session_name = prof_config['sso_session']
338
+ sso_session = cfg["sso-session #{sso_session_name}"]
339
+ unless sso_session
340
+ raise ArgumentError,
341
+ "sso-session #{sso_session_name} must be defined in the config file. " \
342
+ "Referenced by profile #{profile}"
343
+ end
344
+ sso_region = sso_session['sso_region']
345
+ sso_start_url = sso_session['sso_start_url']
346
+
347
+ # validate sso_region and sso_start_url don't conflict if set on profile and session
348
+ if prof_config['sso_region'] && prof_config['sso_region'] != sso_region
349
+ raise ArgumentError,
350
+ "sso-session #{sso_session_name}'s sso_region (#{sso_region}) " \
351
+ "does not match the profile #{profile}'s sso_region (#{prof_config['sso_region']}'"
352
+ end
353
+ if prof_config['sso_start_url'] && prof_config['sso_start_url'] != sso_start_url
354
+ raise ArgumentError,
355
+ "sso-session #{sso_session_name}'s sso_start_url (#{sso_start_url}) " \
356
+ "does not match the profile #{profile}'s sso_start_url (#{prof_config['sso_start_url']}'"
357
+ end
358
+ else
359
+ sso_region = prof_config['sso_region']
360
+ sso_start_url = prof_config['sso_start_url']
361
+ end
318
362
 
319
363
  SSOCredentials.new(
320
- sso_start_url: prof_config['sso_start_url'],
321
- sso_region: prof_config['sso_region'],
322
364
  sso_account_id: prof_config['sso_account_id'],
323
- sso_role_name: prof_config['sso_role_name']
365
+ sso_role_name: prof_config['sso_role_name'],
366
+ sso_session: prof_config['sso_session'],
367
+ sso_region: sso_region,
368
+ sso_start_url: prof_config['sso_start_url']
369
+ )
370
+ end
371
+ end
372
+
373
+ # If the required sso_ profile values are present, attempt to construct
374
+ # SSOTokenProvider
375
+ def sso_token_from_profile(cfg, profile)
376
+ if @parsed_config &&
377
+ (prof_config = cfg[profile]) &&
378
+ !(prof_config.keys & SSO_TOKEN_PROFILE_KEYS).empty?
379
+
380
+ sso_session_name = prof_config['sso_session']
381
+ sso_session = cfg["sso-session #{sso_session_name}"]
382
+ unless sso_session
383
+ raise ArgumentError,
384
+ "sso-session #{sso_session_name} must be defined in the config file." \
385
+ "Referenced by profile #{profile}"
386
+ end
387
+
388
+ unless sso_session['sso_region']
389
+ raise ArgumentError, "sso-session #{sso_session_name} missing required parameter: sso_region"
390
+ end
391
+
392
+ SSOTokenProvider.new(
393
+ sso_session: sso_session_name,
394
+ sso_region: sso_session['sso_region']
324
395
  )
325
396
  end
326
397
  end
@@ -3,24 +3,19 @@
3
3
  module Aws
4
4
  # An auto-refreshing credential provider that assumes a role via
5
5
  # {Aws::SSO::Client#get_role_credentials} using a cached access
6
- # token. This class does NOT implement the SSO login token flow - tokens
7
- # must generated and refreshed separately by running `aws login` from the
8
- # AWS CLI with the correct profile.
9
- #
10
- # The `SSOCredentials` will auto-refresh the AWS credentials from SSO. In
11
- # addition to AWS credentials expiring after a given amount of time, the
12
- # access token generated and cached from `aws login` will also expire.
13
- # Once this token expires, it will not be usable to refresh AWS credentials,
14
- # and another token will be needed. The SDK does not manage refreshing of
15
- # the token value, but this can be done by running `aws login` with the
16
- # correct profile.
6
+ # token. When `sso_session` is specified, token refresh logic from
7
+ # {Aws::SSOTokenProvider} will be used to refresh the token if possible.
8
+ # This class does NOT implement the SSO login token flow - tokens
9
+ # must generated separately by running `aws login` from the
10
+ # AWS CLI with the correct profile. The `SSOCredentials` will
11
+ # auto-refresh the AWS credentials from SSO.
17
12
  #
18
13
  # # You must first run aws sso login --profile your-sso-profile
19
14
  # sso_credentials = Aws::SSOCredentials.new(
20
15
  # sso_account_id: '123456789',
21
16
  # sso_role_name: "role_name",
22
17
  # sso_region: "us-east-1",
23
- # sso_start_url: 'https://your-start-url.awsapps.com/start'
18
+ # sso_session: 'my_sso_session'
24
19
  # )
25
20
  # ec2 = Aws::EC2::Client.new(credentials: sso_credentials)
26
21
  #
@@ -35,7 +30,8 @@ module Aws
35
30
  include RefreshingCredentials
36
31
 
37
32
  # @api private
38
- SSO_REQUIRED_OPTS = [:sso_account_id, :sso_region, :sso_role_name, :sso_start_url].freeze
33
+ LEGACY_REQUIRED_OPTS = [:sso_start_url, :sso_account_id, :sso_region, :sso_role_name].freeze
34
+ TOKEN_PROVIDER_REQUIRED_OPTS = [:sso_session, :sso_account_id, :sso_region, :sso_role_name].freeze
39
35
 
40
36
  # @api private
41
37
  SSO_LOGIN_GUIDANCE = 'The SSO session associated with this profile has '\
@@ -45,17 +41,23 @@ module Aws
45
41
  # @option options [required, String] :sso_account_id The AWS account ID
46
42
  # that temporary AWS credentials will be resolved for
47
43
  #
48
- # @option options [required, String] :sso_region The AWS region where the
49
- # SSO directory for the given sso_start_url is hosted.
50
- #
51
44
  # @option options [required, String] :sso_role_name The corresponding
52
45
  # IAM role in the AWS account that temporary AWS credentials
53
46
  # will be resolved for.
54
47
  #
55
- # @option options [required, String] :sso_start_url The start URL is
56
- # provided by the SSO service via the console and is the URL used to
48
+ # @option options [required, String] :sso_region The AWS region where the
49
+ # SSO directory for the given sso_start_url is hosted.
50
+ #
51
+ # @option options [String] :sso_session The SSO Token used for fetching
52
+ # the token. If provided, refresh logic from the {Aws::SSOTokenProvider}
53
+ # will be used.
54
+ #
55
+ # @option options [String] :sso_start_url (legacy profiles) If provided,
56
+ # legacy token fetch behavior will be used, which does not support
57
+ # token refreshing. The start URL is provided by the SSO
58
+ # service via the console and is the URL used to
57
59
  # login to the SSO directory. This is also sometimes referred to as
58
- # the "User Portal URL"
60
+ # the "User Portal URL".
59
61
  #
60
62
  # @option options [SSO::Client] :client Optional `SSO::Client`. If not
61
63
  # provided, a client will be constructed.
@@ -65,27 +67,52 @@ module Aws
65
67
  # with an instance of this object when
66
68
  # AWS credentials are required and need to be refreshed.
67
69
  def initialize(options = {})
68
-
69
- missing_keys = SSO_REQUIRED_OPTS.select { |k| options[k].nil? }
70
- unless missing_keys.empty?
71
- raise ArgumentError, "Missing required keys: #{missing_keys}"
70
+ options = options.select {|k, v| !v.nil? }
71
+ if (options[:sso_session])
72
+ missing_keys = TOKEN_PROVIDER_REQUIRED_OPTS.select { |k| options[k].nil? }
73
+ unless missing_keys.empty?
74
+ raise ArgumentError, "Missing required keys: #{missing_keys}"
75
+ end
76
+ @legacy = false
77
+ @sso_role_name = options.delete(:sso_role_name)
78
+ @sso_account_id = options.delete(:sso_account_id)
79
+
80
+ # if client has been passed, don't pass through to SSOTokenProvider
81
+ @client = options.delete(:client)
82
+ options.delete(:sso_start_url)
83
+ @token_provider = Aws::SSOTokenProvider.new(options.dup)
84
+ @sso_session = options.delete(:sso_session)
85
+ @sso_region = options.delete(:sso_region)
86
+
87
+ unless @client
88
+ client_opts = {}
89
+ options.each_pair { |k,v| client_opts[k] = v unless CLIENT_EXCLUDE_OPTIONS.include?(k) }
90
+ client_opts[:region] = @sso_region
91
+ client_opts[:credentials] = nil
92
+ @client = Aws::SSO::Client.new(client_opts)
93
+ end
94
+ else # legacy behavior
95
+ missing_keys = LEGACY_REQUIRED_OPTS.select { |k| options[k].nil? }
96
+ unless missing_keys.empty?
97
+ raise ArgumentError, "Missing required keys: #{missing_keys}"
98
+ end
99
+ @legacy = true
100
+ @sso_start_url = options.delete(:sso_start_url)
101
+ @sso_region = options.delete(:sso_region)
102
+ @sso_role_name = options.delete(:sso_role_name)
103
+ @sso_account_id = options.delete(:sso_account_id)
104
+
105
+ # validate we can read the token file
106
+ read_cached_token
107
+
108
+ client_opts = {}
109
+ options.each_pair { |k,v| client_opts[k] = v unless CLIENT_EXCLUDE_OPTIONS.include?(k) }
110
+ client_opts[:region] = @sso_region
111
+ client_opts[:credentials] = nil
112
+
113
+ @client = options[:client] || Aws::SSO::Client.new(client_opts)
72
114
  end
73
115
 
74
- @sso_start_url = options.delete(:sso_start_url)
75
- @sso_region = options.delete(:sso_region)
76
- @sso_role_name = options.delete(:sso_role_name)
77
- @sso_account_id = options.delete(:sso_account_id)
78
-
79
- # validate we can read the token file
80
- read_cached_token
81
-
82
-
83
- client_opts = {}
84
- options.each_pair { |k,v| client_opts[k] = v unless CLIENT_EXCLUDE_OPTIONS.include?(k) }
85
- client_opts[:region] = @sso_region
86
- client_opts[:credentials] = nil
87
-
88
- @client = options[:client] || Aws::SSO::Client.new(client_opts)
89
116
  @async_refresh = true
90
117
  super
91
118
  end
@@ -111,12 +138,20 @@ module Aws
111
138
  end
112
139
 
113
140
  def refresh
114
- cached_token = read_cached_token
115
- c = @client.get_role_credentials(
116
- account_id: @sso_account_id,
117
- role_name: @sso_role_name,
118
- access_token: cached_token['accessToken']
119
- ).role_credentials
141
+ c = if @legacy
142
+ cached_token = read_cached_token
143
+ @client.get_role_credentials(
144
+ account_id: @sso_account_id,
145
+ role_name: @sso_role_name,
146
+ access_token: cached_token['accessToken']
147
+ ).role_credentials
148
+ else
149
+ @client.get_role_credentials(
150
+ account_id: @sso_account_id,
151
+ role_name: @sso_role_name,
152
+ access_token: @token_provider.token.token
153
+ ).role_credentials
154
+ end
120
155
 
121
156
  @credentials = Credentials.new(
122
157
  c.access_key_id,