# frozen_string_literal: true module Aws module Partitions # @api private class EndpointProvider # When sts_regional_endpoint is set to `legacy`, the endpoint # pattern stays global for the following regions: STS_LEGACY_REGIONS = %w[ ap-northeast-1 ap-south-1 ap-southeast-1 ap-southeast-2 aws-global ca-central-1 eu-central-1 eu-north-1 eu-west-1 eu-west-2 eu-west-3 sa-east-1 us-east-1 us-east-2 us-west-1 us-west-2 ].freeze # Intentionally marked private. The format of the endpoint rules # is an implementation detail. # @api private def initialize(rules) @rules = rules end # @param [String] region The region for the client. # @param [String] service The endpoint prefix for the service, e.g. # "monitoring" for cloudwatch. # @param [String] sts_regional_endpoints [STS only] Whether to use # `legacy` (global endpoint for legacy regions) or `regional` mode for # using regional endpoint for supported regions except 'aws-global' # @api private Use the static class methods instead. def resolve(region, service, sts_regional_endpoints) 'https://' + endpoint_for(region, service, sts_regional_endpoints) end # @api private Use the static class methods instead. def signing_region(region, service, sts_regional_endpoints) credential_scope(region, service, sts_regional_endpoints) .fetch('region', region) end # @api private Use the static class methods instead. def signing_service(region, service) # don't default to the service name # signers should prefer the api metadata's signingName # if no service is set in the credentialScope credential_scope(region, service, 'regional') .fetch('service', nil) end # @api private Use the static class methods instead. def credential_scope(region, service, sts_regional_endpoints) partition = get_partition(region) service_cfg = partition.fetch('services', {}) .fetch(service, {}) endpoints = service_cfg.fetch('endpoints', {}) # Check for sts legacy behavior sts_legacy = service == 'sts' && sts_regional_endpoints == 'legacy' && STS_LEGACY_REGIONS.include?(region) is_global = !endpoints.key?(region) && service_cfg['isRegionalized'] == false # Check for global endpoint. if sts_legacy || is_global region = service_cfg.fetch('partitionEndpoint', region) end default_credential_scope = service_cfg .fetch('defaults', {}) .fetch('credentialScope', {}) endpoints .fetch(region, {}) .fetch('credentialScope', default_credential_scope) end # @api private Use the static class methods instead. def dns_suffix_for(region) get_partition(region)['dnsSuffix'] end private def endpoint_for(region, service, sts_regional_endpoints) partition = get_partition(region) service_cfg = partition.fetch('services', {}).fetch(service, {}) # Find the default endpoint default_endpoint = service_cfg .fetch('defaults', {}) .fetch('hostname', partition['defaults']['hostname']) endpoints = service_cfg.fetch('endpoints', {}) # Check for sts legacy behavior sts_legacy = service == 'sts' && sts_regional_endpoints == 'legacy' && STS_LEGACY_REGIONS.include?(region) is_global = !endpoints.key?(region) && service_cfg['isRegionalized'] == false # Check for global endpoint. if sts_legacy || is_global region = service_cfg.fetch('partitionEndpoint', region) end # Check for service/region level endpoint. endpoint = endpoints .fetch(region, {}) .fetch('hostname', default_endpoint) # Replace placeholders from the endpoints endpoint.sub('{region}', region) .sub('{service}', service) .sub('{dnsSuffix}', partition['dnsSuffix']) end def get_partition(region_or_partition) partition_containing_region(region_or_partition) || partition_matching_region(region_or_partition) || partition_matching_name(region_or_partition) || default_partition end def partition_containing_region(region) @rules['partitions'].find do |p| p['regions'].key?(region) end end def partition_matching_region(region) @rules['partitions'].find do |p| region.match(p['regionRegex']) || p['services'].values.find do |svc| svc['endpoints'].key?(region) if svc.key?('endpoints') end end end def partition_matching_name(partition_name) @rules['partitions'].find { |p| p['partition'] == partition_name } end def default_partition @rules['partitions'].find { |p| p['partition'] == 'aws' } || @rules['partitions'].first end class << self def resolve(region, service, sts_regional_endpoints = 'regional') default_provider.resolve(region, service, sts_regional_endpoints) end def signing_region(region, service, sts_regional_endpoints = 'regional') default_provider.signing_region(region, service, sts_regional_endpoints) end def signing_service(region, service) default_provider.signing_service(region, service) end def dns_suffix_for(region) default_provider.dns_suffix_for(region) end private def default_provider @default_provider ||= EndpointProvider.new(Partitions.defaults) end end end end end