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

Support Auth trait for Sigv4a #2923

Open
wants to merge 13 commits into
base: version-3
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def initialize(options)
@protocol_settings = api.fetch('metadata')['protocolSettings'] || {}
@api_version = api.fetch('metadata')['apiVersion']
@signature_version = api.fetch('metadata')['signatureVersion']
@auth = api.fetch('metadata')['auth']
@full_name = api.fetch('metadata')['serviceFullName']
@short_name = api.fetch('metadata')['serviceAbbreviation'] || @full_name
@require_endpoint_discovery = api.fetch('operations', []).any? do |_, o|
Expand Down Expand Up @@ -136,6 +137,9 @@ def included_in_core?
# @return [String] The signature version, e.g. "v4"
attr_reader :signature_version

# @return [Array<String>] A list of supported auth types
attr_reader :auth

# @return [String] The full product name for the service,
# e.g. "Amazon Simple Storage Service".
attr_reader :full_name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ClientApiModule < View
# keep all
'endpointPrefix' => true,
'signatureVersion' => true,
'auth' => true,
'signingName' => true,
'serviceFullName' => true,
'protocol' => true,
Expand Down Expand Up @@ -246,6 +247,7 @@ def operations
end
o.authorizer = operation['authorizer'] if operation.key?('authorizer')
o.authtype = operation['authtype'] if operation.key?('authtype')
o.auth = operation['auth'] if operation.key?('auth')
o.require_apikey = operation['requiresApiKey'] if operation.key?('requiresApiKey')
o.pager = pager(operation_name)
o.async = @service.protocol_settings['h2'] == 'eventstream' &&
Expand Down Expand Up @@ -581,6 +583,9 @@ def initialize
# @return [String,nil]
attr_accessor :authtype

# @return [Array<String>]
attr_accessor :auth

# @return [Boolean]
attr_accessor :endpoint_trait

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
describe 'Client Interface:' do
describe 'SignatureVersion: bearer' do
before(:all) do
SpecHelper.generate_service(['BearerAuth'], multiple_files: false)
SpecHelper.generate_service(['LegacySignBearerAuth'], multiple_files: false)
end

let(:token) { 'token' }

let(:token_provider) { Aws::StaticTokenProvider.new(token) }

let(:client) do
BearerAuth::Client.new(
LegacySignBearerAuth::Client.new(
region: 'us-west-2',
stub_responses: true,
token_provider: token_provider,
Expand Down Expand Up @@ -51,15 +51,15 @@

describe 'SignatureVersion: v4' do
before(:all) do
SpecHelper.generate_service(['V4WithBearer'], multiple_files: false)
SpecHelper.generate_service(['LegacySignV4WithBearer'], multiple_files: false)
end

let(:token) { 'token' }

let(:token_provider) { Aws::StaticTokenProvider.new(token) }

let(:client) do
V4WithBearer::Client.new(
LegacySignV4WithBearer::Client.new(
region: 'us-west-2',
stub_responses: true,
token_provider: token_provider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@
end

let(:client) do
TransferEncoding::Client.new(
region: 'us-west-2',
access_key_id: 'akid',
secret_access_key: 'secret',
stub_responses: true,
endpoint: 'https://svc.us-west-2.amazonaws.com'
)
TransferEncoding::Client.new(stub_responses: true)
end

it 'adds `Transfer-Encoding` header for `v4-unsigned-body` auth types' do
Expand Down Expand Up @@ -69,6 +63,5 @@
resp = client.non_streaming(body: 'heyhey')
expect(resp.context.http_request.headers['Content-Length']).to eq('19')
end

end
end
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ module {{module_name}}
{{#authtype}}
o['authtype'] = "{{.}}"
{{/authtype}}
{{#auth}}
o['auth'] = {{&auth}}
{{/auth}}
{{#endpoint_trait}}
o.endpoint_pattern = {
{{#endpoint_pattern}}
Expand Down
15 changes: 11 additions & 4 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.184.0"
MINIMUM_CORE_VERSION = "3.186.0"

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

EVENTSTREAM_PLUGIN = "Aws::Plugins::EventStreamConfiguration"

Expand Down Expand Up @@ -151,9 +151,16 @@ def gem_dependencies(api, dependencies)
core_version_string = "', '>= #{min_core}"
dependencies['aws-sdk-core'] = "~> #{version_file.split('.')[0]}#{core_version_string}"

api['metadata'].fetch('auth', []).each do |auth|
if %w[aws.auth#sigv4 aws.auth#sigv4a].include?(auth)
dependencies['aws-sigv4'] = '~> 1.5'
end
end

# deprecated auth but a reasonable fallback
case api['metadata']['signatureVersion']
when 'v4' then dependencies['aws-sigv4'] = '~> 1.1'
when 'v2' then dependencies['aws-sigv2'] = '~> 1.0'
when 'v4' then dependencies['aws-sigv4'] ||= '~> 1.1'
when 'v2' then dependencies['aws-sigv2'] ||= '~> 1.0'
end
dependencies
end
Expand Down
4 changes: 4 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 `auth` trait to enable SigV4a based services.

* Feature - Support configuration for sigv4a signing regions using `ENV['AWS_SIGV4A_SIGNING_REGION_SET']`, `sigv4a_signing_region_set` shared config, or the `sigv4a_signing_region_set` client option.

3.185.1 (2023-10-05)
------------------

Expand Down
82 changes: 64 additions & 18 deletions gems/aws-sdk-core/lib/aws-sdk-core/endpoints.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@
require_relative 'endpoints/tree_rule'
require_relative 'endpoints/url'

require 'aws-sigv4'

module Aws
# @api private
module Endpoints
supported_auth_traits = %w[aws.auth#sigv4 smithy.api#httpBearerAuth smithy.auth#noAuth]
supported_auth_traits += ['aws.auth#sigv4a'] if Aws::Sigv4::Signer.use_crt?
SUPPORTED_AUTH_TRAITS = supported_auth_traits.freeze

class << self
def resolve_auth_scheme(context, endpoint)
if endpoint && (auth_schemes = endpoint.properties['authSchemes'])
Expand All @@ -33,8 +39,64 @@ def resolve_auth_scheme(context, endpoint)

private

def merge_signing_defaults(auth_scheme, config)
if %w[sigv4 sigv4a].include?(auth_scheme['name'])
auth_scheme['signingName'] ||= sigv4_name(config)
if auth_scheme['name'] == 'sigv4a'
# config option supersedes endpoint properties
auth_scheme['signingRegionSet'] =
config.sigv4a_signing_region_set || auth_scheme['signingRegionSet'] || [config.region]
else
auth_scheme['signingRegion'] ||= config.region
end
end
auth_scheme
end

def sigv4_name(config)
config.api.metadata['signingName'] ||
config.api.metadata['endpointPrefix']
end

def default_auth_scheme(context)
case default_api_authtype(context)
if (auth_list = default_api_auth(context))
auth = auth_list.find { |a| SUPPORTED_AUTH_TRAITS.include?(a) }
case auth
when 'aws.auth#sigv4', 'aws.auth#sigv4a'
auth_scheme = { 'name' => auth.split('#').last }
if s3_or_s3v4_signature_version?(context)
auth_scheme = auth_scheme.merge(
'disableDoubleEncoding' => true,
'disableNormalizePath' => true
)
end
merge_signing_defaults(auth_scheme, context.config)
when 'smithy.api#httpBearerAuth'
{ 'name' => 'bearer' }
when 'smithy.auth#noAuth'
{ 'name' => 'none' }
else
raise 'No supported auth trait for this endpoint.'
end
else
legacy_default_auth_scheme(context)
end
end

def default_api_auth(context)
context.config.api.operation(context.operation_name)['auth'] ||
context.config.api.metadata['auth']
end

def s3_or_s3v4_signature_version?(context)
%w[s3 s3v4].include?(context.config.api.metadata['signatureVersion'])
end

# Legacy auth resolution - looks for deprecated signatureVersion
# and authType traits.

def legacy_default_auth_scheme(context)
case legacy_default_api_authtype(context)
when 'v4', 'v4-unsigned-body'
auth_scheme = { 'name' => 'sigv4' }
merge_signing_defaults(auth_scheme, context.config)
Expand All @@ -52,27 +114,11 @@ def default_auth_scheme(context)
end
end

def merge_signing_defaults(auth_scheme, config)
if %w[sigv4 sigv4a].include?(auth_scheme['name'])
auth_scheme['signingName'] ||= sigv4_name(config)
if auth_scheme['name'] == 'sigv4a'
auth_scheme['signingRegionSet'] ||= ['*']
else
auth_scheme['signingRegion'] ||= config.region
end
end
auth_scheme
end

def default_api_authtype(context)
def legacy_default_api_authtype(context)
context.config.api.operation(context.operation_name)['authtype'] ||
context.config.api.metadata['signatureVersion']
end

def sigv4_name(config)
config.api.metadata['signingName'] ||
config.api.metadata['endpointPrefix']
end
end
end
end
9 changes: 9 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ def initialize(*args)
end
end

# Raised when a client is constructed and the sigv4a region set is invalid.
# It is invalid when it is empty and/or contains empty strings.
class InvalidRegionSetError < ArgumentError
def initialize(*args)
msg = 'The provided sigv4a region set was empty or invalid.'
super(msg)
end
end

# Raised when a client is contsructed and the region is not valid.
class InvalidRegionError < ArgumentError
def initialize(*args)
Expand Down
34 changes: 31 additions & 3 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 @@ -24,6 +24,20 @@ class RegionalEndpoint < Seahorse::Client::Plugin
resolve_region(cfg)
end

option(:sigv4a_signing_region_set,
doc_type: Array,
docstring: <<-DOCS) do |cfg|
A list of regions that should be signed with SigV4a signing. When
not passed, a default `:sigv4a_signing_region_set` is searched for
in the following locations:

* `Aws.config[:sigv4a_signing_region_set]`
* `ENV['AWS_SIGV4A_SIGNING_REGION_SET']`
* `~/.aws/config`
DOCS
resolve_sigv4a_signing_region_set(cfg)
end

option(:use_dualstack_endpoint,
doc_type: 'Boolean',
docstring: <<-DOCS) do |cfg|
Expand Down Expand Up @@ -65,9 +79,17 @@ class RegionalEndpoint < Seahorse::Client::Plugin
end

def after_initialize(client)
if client.config.region.nil? || client.config.region == ''
raise Errors::MissingRegionError
end
region = client.config.region
raise Errors::MissingRegionError if region.nil? || region == ''

region_set = client.config.sigv4a_signing_region_set
return if region_set.nil?
raise Errors::InvalidRegionSetError unless region_set.is_a?(Array)

region_set = region_set.compact.reject(&:empty?)
raise Errors::InvalidRegionSetError if region_set.empty?

client.config.sigv4a_signing_region_set = region_set
end

class << self
Expand All @@ -81,6 +103,12 @@ def resolve_region(cfg)
env_region || cfg_region
end

def resolve_sigv4a_signing_region_set(cfg)
value = ENV['AWS_SIGV4A_SIGNING_REGION_SET']
value ||= Aws.shared_config.sigv4a_signing_region_set(profile: cfg.profile)
value.split(',') if value
end

def resolve_use_dualstack_endpoint(cfg)
value = ENV['AWS_USE_DUALSTACK_ENDPOINT']
value ||= Aws.shared_config.use_dualstack_endpoint(
Expand Down
2 changes: 1 addition & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/plugins/sign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def initialize(auth_scheme, config, region_override = nil)
end

region = if scheme_name == 'sigv4a'
auth_scheme['signingRegionSet'].first
auth_scheme['signingRegionSet'].join(',')
mullermp marked this conversation as resolved.
Show resolved Hide resolved
else
auth_scheme['signingRegion']
end
Expand Down
1 change: 1 addition & 0 deletions 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,
:sigv4a_signing_region_set,
:ca_bundle,
:credential_process,
:endpoint_discovery_enabled,
Expand Down