Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Account id endpoint support #2936

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ def initialize(options)

# @return [Array<EndpointParameter>]
attr_reader :parameters

def has_endpoint_built_in?
parameters.any? { |p| p.param_data['builtIn'] == 'SDK::Endpoint' }
end
end

class EndpointParameter
Expand Down Expand Up @@ -134,6 +130,10 @@ def built_in_client_context_param_value(param_data)
else
'context.config.use_dualstack_endpoint'
end
when 'AWS::Auth::AccountId'
'context.config.credentials.credentials.account_id'
when 'AWS::Auth::AccountIdEndpointMode'
'context.config.account_id_endpoint_mode'
when 'AWS::STS::UseGlobalEndpoint'
"context.config.sts_regional_endpoints == 'legacy'"
when 'AWS::S3::UseGlobalEndpoint'
Expand All @@ -151,7 +151,7 @@ def built_in_client_context_param_value(param_data)
when 'AWS::S3::DisableMultiRegionAccessPoints'
'context.config.s3_disable_multiregion_access_points'
when 'SDK::Endpoint'
'endpoint'
'context.config.regional_endpoint ? nil : context.config.endpoint.to_s'
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ def initialize(options)
operation_name: Underscore.underscore(
operation_inputs_test['operationName']
),
operation_params: operation_inputs_test['operationParams'] || [],
built_in_params: operation_inputs_test['builtInParams'] || [],
client_params: operation_inputs_test['clientParams'] || []
operation_params: operation_inputs_test['operationParams'] || {},
built_in_params: operation_inputs_test['builtInParams'] || {},
client_params: operation_inputs_test['clientParams'] || {}
)
end
end
Expand Down Expand Up @@ -117,12 +117,13 @@ def initialize(options)
@client_params = options[:client_params].map do |k,v|
Param.new(Underscore.underscore(k), v)
end

@client_params += options[:built_in_params].map do |k,v|
built_in_to_param(k, v)
end
# the expected default of UseGlobalEndpoint does not match the SDK's default value
if @service.identifier == 's3' && !options[:built_in_params].include?('AWS::S3::UseGlobalEndpoint')
# the expected default of UseGlobalEndpoint in rules
# does not match the Ruby SDK's default value
if @service.identifier == 's3' &&
!options[:built_in_params].include?('AWS::S3::UseGlobalEndpoint')
@client_params << built_in_to_param('AWS::S3::UseGlobalEndpoint', false)
end
end
Expand Down Expand Up @@ -158,6 +159,14 @@ def built_in_to_param(built_in, value)
Param.new('use_fips_endpoint', value)
when 'AWS::UseDualStack'
Param.new('use_dualstack_endpoint', value)
when 'AWS::Auth::AccountId'
Param.new(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably practically don't need to care about this - but what if multiple Auth builtins are set (ie, accountId + credentialScope)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? If both are set, they are both set as parameters. Do you mean passing in "credentials" and having something delegate to either of those?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both are set then we would have two credentials params which would end up not working correctly. At least as of right now, I think they would generally be mutually exclusive, so shouldn't matter...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked offline, keeping as TODO when either feature is merged.

'credentials',
"Aws::Credentials.new('stubbed-akid', 'stubbed-secret', account_id: '#{value}')",
true
)
when 'AWS::Auth::AccountIdEndpointMode'
Param.new('account_id_endpoint_mode', value)
when 'AWS::STS::UseGlobalEndpoint'
Param.new('sts_regional_endpoints', value ? 'legacy' : 'regional')
when 'AWS::S3::UseGlobalEndpoint'
Expand All @@ -166,9 +175,7 @@ def built_in_to_param(built_in, value)
Param.new('use_accelerate_endpoint', value)
when 'AWS::S3::ForcePathStyle'
Param.new('force_path_style', value)
when 'AWS::S3::UseArnRegion'
Param.new('s3_use_arn_region', value)
when 'AWS::S3Control::UseArnRegion'
when 'AWS::S3::UseArnRegion', 'AWS::S3Control::UseArnRegion'
Param.new('s3_use_arn_region', value)
when 'AWS::S3::DisableMultiRegionAccessPoints'
Param.new('s3_disable_multiregion_access_points', value)
Expand All @@ -181,14 +188,16 @@ def built_in_to_param(built_in, value)
end

class Param
def initialize(param, value)
def initialize(param, value, literal = false)
@param = param
@value = value
@literal = literal
end

attr_accessor :param

def value
if @value.is_a? String
if @value.is_a?(String) && !@literal
"'#{@value}'"
else
@value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ module {{module_name}}
{{#endpoint_classes}}
class {{name}}
def self.build(context)
{{#has_endpoint_built_in?}}
unless context.config.regional_endpoint
endpoint = context.config.endpoint.to_s
end
{{/has_endpoint_built_in?}}
{{module_name}}::EndpointParameters.new(
{{#parameters}}
{{#static_string?}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module {{module_name}}
subject { {{module_name}}::EndpointProvider.new }

{{#endpoint_tests}}
context '{{documentation}}' do
context "{{{documentation}}}" do
let(:expected) do
{{{expect}}}
end
Expand Down
4 changes: 2 additions & 2 deletions build_tools/services.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class ServiceEnumerator
MANIFEST_PATH = File.expand_path('../../services.json', __FILE__)

# Minimum `aws-sdk-core` version for new gem builds
MINIMUM_CORE_VERSION = "3.193.0"
MINIMUM_CORE_VERSION = "3.194.0"

# Minimum `aws-sdk-core` version for new S3 gem builds
MINIMUM_CORE_VERSION_S3 = "3.193.0"
MINIMUM_CORE_VERSION_S3 = "3.194.0"

EVENTSTREAM_PLUGIN = "Aws::Plugins::EventStreamConfiguration"

Expand Down
5 changes: 5 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Unreleased Changes
------------------

* Feature - Support Account ID credentials using `ENV['AWS_ACCOUNT_ID']`, `aws_account_id` shared config, or the `account_id` Client configuration option.

* Feature - Support Account ID endpoint mode using `ENV['AWS_ACCOUNT_ID_ENDPOINT_MODE']`, `aws_account_id_endpoint_mode` shared config, or the `account_id_endpoint_mode` Client configuration option. Defaults to `preferred`, which will use the account id endpoint if available. Set to `disabled` to disable account id endpoints. Set to `required` to require account id endpoint usage; an error is raised if credentials do not have an account id.

3.193.0 (2024-04-25)
------------------

Expand Down Expand Up @@ -62,6 +66,7 @@ Unreleased Changes
------------------

* Issue - Ensure output unions work correctly with stub_responses.
>>>>>>> version-3

3.191.3 (2024-02-20)
------------------
Expand Down
12 changes: 7 additions & 5 deletions gems/aws-sdk-core/lib/aws-sdk-core/assume_role_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@ def initialize(options = {})
private

def refresh
c = @client.assume_role(@assume_role_params).credentials
c = @client.assume_role(@assume_role_params)
creds = c.credentials
@credentials = Credentials.new(
c.access_key_id,
c.secret_access_key,
c.session_token
creds.access_key_id,
creds.secret_access_key,
creds.session_token,
account_id: ARNParser.parse(c.assumed_role_user.arn).account_id
)
@expiration = c.expiration
@expiration = creds.expiration
end

class << self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,15 @@ def refresh
# read from token file everytime it refreshes
@assume_role_web_identity_params[:web_identity_token] = _token_from_file(@token_file)

c = @client.assume_role_with_web_identity(
@assume_role_web_identity_params).credentials
c = @client.assume_role_with_web_identity(@assume_role_web_identity_params)
creds = c.credentials
@credentials = Credentials.new(
c.access_key_id,
c.secret_access_key,
c.session_token
creds.access_key_id,
creds.secret_access_key,
creds.session_token,
account_id: ARNParser.parse(c.assumed_role_user.arn).account_id
)
@expiration = c.expiration
@expiration = creds.expiration
end

def _token_from_file(path)
Expand Down
11 changes: 9 additions & 2 deletions gems/aws-sdk-core/lib/aws-sdk-core/credential_provider_chain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ def static_credentials(options)
Credentials.new(
options[:config].access_key_id,
options[:config].secret_access_key,
options[:config].session_token
options[:config].session_token,
account_id: options[:config].account_id
)
end
end
Expand Down Expand Up @@ -94,7 +95,13 @@ def env_credentials(_options)
key = %w[AWS_ACCESS_KEY_ID AMAZON_ACCESS_KEY_ID AWS_ACCESS_KEY]
secret = %w[AWS_SECRET_ACCESS_KEY AMAZON_SECRET_ACCESS_KEY AWS_SECRET_KEY]
token = %w[AWS_SESSION_TOKEN AMAZON_SESSION_TOKEN]
Credentials.new(envar(key), envar(secret), envar(token))
account_id = %w[AWS_ACCOUNT_ID]
Credentials.new(
envar(key),
envar(secret),
envar(token),
account_id: envar(account_id)
)
end

def envar(keys)
Expand Down
19 changes: 13 additions & 6 deletions gems/aws-sdk-core/lib/aws-sdk-core/credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,28 @@ class Credentials
# @param [String] access_key_id
# @param [String] secret_access_key
# @param [String] session_token (nil)
def initialize(access_key_id, secret_access_key, session_token = nil)
# @param [Hash] kwargs
# @option kwargs [String] :credential_scope (nil)
def initialize(access_key_id, secret_access_key, session_token = nil,
**kwargs)
@access_key_id = access_key_id
@secret_access_key = secret_access_key
@session_token = session_token
@account_id = kwargs[:account_id]
end

# @return [String, nil]
# @return [String]
attr_reader :access_key_id

# @return [String, nil]
# @return [String]
attr_reader :secret_access_key

# @return [String, nil]
attr_reader :session_token

# @return [String, nil]
attr_reader :account_id

# @return [Credentials]
def credentials
self
Expand All @@ -30,9 +37,9 @@ def credentials
# access key are both set.
def set?
!access_key_id.nil? &&
!access_key_id.empty? &&
!secret_access_key.nil? &&
!secret_access_key.empty?
!access_key_id.empty? &&
!secret_access_key.nil? &&
!secret_access_key.empty?
end

# Removing the secret access key from the default inspect string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class CredentialsConfiguration < Seahorse::Client::Plugin

option(:session_token, doc_type: String, docstring: '')

option(:account_id, doc_type: String, docstring: '')

option(:profile,
doc_default: 'default',
doc_type: String,
Expand Down Expand Up @@ -58,13 +60,15 @@ class CredentialsConfiguration < Seahorse::Client::Plugin
locations will be searched for credentials:

* `Aws.config[:credentials]`
* The `:access_key_id`, `:secret_access_key`, and `:session_token` options.
* ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']
* The `:access_key_id`, `:secret_access_key`, `:session_token`, and
`:account_id` options.
* ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'],
ENV['AWS_SESSION_TOKEN'], and ENV['AWS_ACCOUNT_ID']
* `~/.aws/credentials`
* `~/.aws/config`
* EC2/ECS IMDS instance profile - When used by default, the timeouts
are very aggressive. Construct and pass an instance of
`Aws::InstanceProfileCredentails` or `Aws::ECSCredentials` to
`Aws::InstanceProfileCredentials` or `Aws::ECSCredentials` to
enable retries and extended timeouts. Instance profile credential
fetching can be disabled by setting ENV['AWS_EC2_METADATA_DISABLED']
to true.
Expand Down
20 changes: 20 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/plugins/regional_endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ class RegionalEndpoint < Seahorse::Client::Plugin
resolve_ignore_configured_endpoint_urls(cfg)
end

option(:account_id_endpoint_mode,
doc_type: String,
docstring: <<-DOCS) do |cfg|
The account ID endpoint mode to use. This can be one of the following values:
* `preferred` - The default behavior. Use the account ID endpoint if
available, otherwise use the standard endpoint.
* `disabled` - Never use the account ID endpoint. Only use the standard
endpoint.
* `required` - Always use the account ID endpoint. If the account ID
cannot be retrieved from credentials, an error is raised.
DOCS
resolve_account_id_endpoint_mode(cfg)
end

option(:endpoint, doc_type: String, docstring: <<-DOCS) do |cfg|
The client endpoint is normally constructed from the `:region`
option. You should only configure an `:endpoint` when connecting
Expand Down Expand Up @@ -101,6 +115,12 @@ def resolve_ignore_configured_endpoint_urls(cfg)
Aws::Util.str_2_bool(value&.downcase) || false
end

def resolve_account_id_endpoint_mode(cfg)
value = ENV['AWS_ACCOUNT_ID_ENDPOINT_MODE']
value ||= Aws.shared_config.account_id_endpoint_mode(profile: cfg.profile)
value || 'preferred'
end

# NOTE: with Endpoints 2.0, some of this logic is deprecated
# but because new old service gems may depend on new core versions
# we must preserve that behavior.
Expand Down
3 changes: 2 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/process_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def _parse_payload_format_v1(creds_json)
creds = Credentials.new(
creds_json['AccessKeyId'],
creds_json['SecretAccessKey'],
creds_json['SessionToken']
creds_json['SessionToken'],
account_id: creds_json['AccountId']
)

@expiration = creds_json['Expiration'] ? Time.iso8601(creds_json['Expiration']) : nil
Expand Down
4 changes: 3 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/shared_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ def self.config_reader(*attrs)

config_reader(
:region,
:account_id_endpoint_mode,
:ca_bundle,
:credential_process,
:endpoint_discovery_enabled,
Expand Down Expand Up @@ -413,7 +414,8 @@ def credentials_from_profile(prof_config)
creds = Credentials.new(
prof_config['aws_access_key_id'],
prof_config['aws_secret_access_key'],
prof_config['aws_session_token']
prof_config['aws_session_token'],
account_id: prof_config['aws_account_id']
)
creds if creds.set?
end
Expand Down
7 changes: 0 additions & 7 deletions gems/aws-sdk-core/lib/aws-sdk-core/shared_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ class SharedCredentials

include CredentialProvider

# @api private
KEY_MAP = {
'aws_access_key_id' => 'access_key_id',
'aws_secret_access_key' => 'secret_access_key',
'aws_session_token' => 'session_token',
}

# Constructs a new SharedCredentials object. This will load static
# (access_key_id, secret_access_key and session_token) AWS access
# credentials from an ini file, which supports profiles. The default
Expand Down
3 changes: 2 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/sso_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ def refresh
@credentials = Credentials.new(
c.access_key_id,
c.secret_access_key,
c.session_token
c.session_token,
account_id: @sso_account_id
)
@expiration = Time.at(c.expiration / 1000.0)
end
Expand Down
Loading
Loading