aws-sdk-core 3.131.1 → 3.168.1

Sign up to get free protection for your applications and to get access to all the features.
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,