Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/botocore/regions.py: 21%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"). You
4# may not use this file except in compliance with the License. A copy of
5# the License is located at
6#
7# http://aws.amazon.com/apache2.0/
8#
9# or in the "license" file accompanying this file. This file is
10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11# ANY KIND, either express or implied. See the License for the specific
12# language governing permissions and limitations under the License.
13"""Resolves regions and endpoints.
15This module implements endpoint resolution, including resolving endpoints for a
16given service and region and resolving the available endpoints for a service
17in a specific AWS partition.
18"""
20import copy
21import logging
22import re
23from enum import Enum
25from botocore import UNSIGNED, xform_name
26from botocore.auth import AUTH_TYPE_MAPS, HAS_CRT
27from botocore.crt import CRT_SUPPORTED_AUTH_TYPES
28from botocore.endpoint_provider import EndpointProvider
29from botocore.exceptions import (
30 EndpointProviderError,
31 EndpointVariantError,
32 InvalidEndpointConfigurationError,
33 InvalidHostLabelError,
34 MissingDependencyException,
35 NoRegionError,
36 ParamValidationError,
37 UnknownEndpointResolutionBuiltInName,
38 UnknownRegionError,
39 UnknownSignatureVersionError,
40 UnsupportedS3AccesspointConfigurationError,
41 UnsupportedS3ConfigurationError,
42 UnsupportedS3ControlArnError,
43 UnsupportedS3ControlConfigurationError,
44)
45from botocore.utils import ensure_boolean, instance_cache
47LOG = logging.getLogger(__name__)
48DEFAULT_URI_TEMPLATE = '{service}.{region}.{dnsSuffix}' # noqa
49DEFAULT_SERVICE_DATA = {'endpoints': {}}
52class BaseEndpointResolver:
53 """Resolves regions and endpoints. Must be subclassed."""
55 def construct_endpoint(self, service_name, region_name=None):
56 """Resolves an endpoint for a service and region combination.
58 :type service_name: string
59 :param service_name: Name of the service to resolve an endpoint for
60 (e.g., s3)
62 :type region_name: string
63 :param region_name: Region/endpoint name to resolve (e.g., us-east-1)
64 if no region is provided, the first found partition-wide endpoint
65 will be used if available.
67 :rtype: dict
68 :return: Returns a dict containing the following keys:
69 - partition: (string, required) Resolved partition name
70 - endpointName: (string, required) Resolved endpoint name
71 - hostname: (string, required) Hostname to use for this endpoint
72 - sslCommonName: (string) sslCommonName to use for this endpoint.
73 - credentialScope: (dict) Signature version 4 credential scope
74 - region: (string) region name override when signing.
75 - service: (string) service name override when signing.
76 - signatureVersions: (list<string>) A list of possible signature
77 versions, including s3, v4, v2, and s3v4
78 - protocols: (list<string>) A list of supported protocols
79 (e.g., http, https)
80 - ...: Other keys may be included as well based on the metadata
81 """
82 raise NotImplementedError
84 def get_available_partitions(self):
85 """Lists the partitions available to the endpoint resolver.
87 :return: Returns a list of partition names (e.g., ["aws", "aws-cn"]).
88 """
89 raise NotImplementedError
91 def get_available_endpoints(
92 self, service_name, partition_name='aws', allow_non_regional=False
93 ):
94 """Lists the endpoint names of a particular partition.
96 :type service_name: string
97 :param service_name: Name of a service to list endpoint for (e.g., s3)
99 :type partition_name: string
100 :param partition_name: Name of the partition to limit endpoints to.
101 (e.g., aws for the public AWS endpoints, aws-cn for AWS China
102 endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.
104 :type allow_non_regional: bool
105 :param allow_non_regional: Set to True to include endpoints that are
106 not regional endpoints (e.g., s3-external-1,
107 fips-us-gov-west-1, etc).
108 :return: Returns a list of endpoint names (e.g., ["us-east-1"]).
109 """
110 raise NotImplementedError
113class EndpointResolver(BaseEndpointResolver):
114 """Resolves endpoints based on partition endpoint metadata"""
116 _UNSUPPORTED_DUALSTACK_PARTITIONS = ['aws-iso', 'aws-iso-b']
118 def __init__(self, endpoint_data, uses_builtin_data=False):
119 """
120 :type endpoint_data: dict
121 :param endpoint_data: A dict of partition data.
123 :type uses_builtin_data: boolean
124 :param uses_builtin_data: Whether the endpoint data originates in the
125 package's data directory.
126 """
127 if 'partitions' not in endpoint_data:
128 raise ValueError('Missing "partitions" in endpoint data')
129 self._endpoint_data = endpoint_data
130 self.uses_builtin_data = uses_builtin_data
132 def get_service_endpoints_data(self, service_name, partition_name='aws'):
133 for partition in self._endpoint_data['partitions']:
134 if partition['partition'] != partition_name:
135 continue
136 services = partition['services']
137 if service_name not in services:
138 continue
139 return services[service_name]['endpoints']
141 def get_available_partitions(self):
142 result = []
143 for partition in self._endpoint_data['partitions']:
144 result.append(partition['partition'])
145 return result
147 def get_available_endpoints(
148 self,
149 service_name,
150 partition_name='aws',
151 allow_non_regional=False,
152 endpoint_variant_tags=None,
153 ):
154 result = []
155 for partition in self._endpoint_data['partitions']:
156 if partition['partition'] != partition_name:
157 continue
158 services = partition['services']
159 if service_name not in services:
160 continue
161 service_endpoints = services[service_name]['endpoints']
162 for endpoint_name in service_endpoints:
163 is_regional_endpoint = endpoint_name in partition['regions']
164 # Only regional endpoints can be modeled with variants
165 if endpoint_variant_tags and is_regional_endpoint:
166 variant_data = self._retrieve_variant_data(
167 service_endpoints[endpoint_name], endpoint_variant_tags
168 )
169 if variant_data:
170 result.append(endpoint_name)
171 elif allow_non_regional or is_regional_endpoint:
172 result.append(endpoint_name)
173 return result
175 def get_partition_dns_suffix(
176 self, partition_name, endpoint_variant_tags=None
177 ):
178 for partition in self._endpoint_data['partitions']:
179 if partition['partition'] == partition_name:
180 if endpoint_variant_tags:
181 variant = self._retrieve_variant_data(
182 partition.get('defaults'), endpoint_variant_tags
183 )
184 if variant and 'dnsSuffix' in variant:
185 return variant['dnsSuffix']
186 else:
187 return partition['dnsSuffix']
188 return None
190 def construct_endpoint(
191 self,
192 service_name,
193 region_name=None,
194 partition_name=None,
195 use_dualstack_endpoint=False,
196 use_fips_endpoint=False,
197 ):
198 if (
199 service_name == 's3'
200 and use_dualstack_endpoint
201 and region_name is None
202 ):
203 region_name = 'us-east-1'
205 if partition_name is not None:
206 valid_partition = None
207 for partition in self._endpoint_data['partitions']:
208 if partition['partition'] == partition_name:
209 valid_partition = partition
211 if valid_partition is not None:
212 result = self._endpoint_for_partition(
213 valid_partition,
214 service_name,
215 region_name,
216 use_dualstack_endpoint,
217 use_fips_endpoint,
218 True,
219 )
220 return result
221 return None
223 # Iterate over each partition until a match is found.
224 for partition in self._endpoint_data['partitions']:
225 if use_dualstack_endpoint and (
226 partition['partition']
227 in self._UNSUPPORTED_DUALSTACK_PARTITIONS
228 ):
229 continue
230 result = self._endpoint_for_partition(
231 partition,
232 service_name,
233 region_name,
234 use_dualstack_endpoint,
235 use_fips_endpoint,
236 )
237 if result:
238 return result
240 def get_partition_for_region(self, region_name):
241 for partition in self._endpoint_data['partitions']:
242 if self._region_match(partition, region_name):
243 return partition['partition']
244 raise UnknownRegionError(
245 region_name=region_name,
246 error_msg='No partition found for provided region_name.',
247 )
249 def _endpoint_for_partition(
250 self,
251 partition,
252 service_name,
253 region_name,
254 use_dualstack_endpoint,
255 use_fips_endpoint,
256 force_partition=False,
257 ):
258 partition_name = partition["partition"]
259 if (
260 use_dualstack_endpoint
261 and partition_name in self._UNSUPPORTED_DUALSTACK_PARTITIONS
262 ):
263 error_msg = (
264 "Dualstack endpoints are currently not supported"
265 f" for {partition_name} partition"
266 )
267 raise EndpointVariantError(tags=['dualstack'], error_msg=error_msg)
269 # Get the service from the partition, or an empty template.
270 service_data = partition['services'].get(
271 service_name, DEFAULT_SERVICE_DATA
272 )
273 # Use the partition endpoint if no region is supplied.
274 if region_name is None:
275 if 'partitionEndpoint' in service_data:
276 region_name = service_data['partitionEndpoint']
277 else:
278 raise NoRegionError()
280 resolve_kwargs = {
281 'partition': partition,
282 'service_name': service_name,
283 'service_data': service_data,
284 'endpoint_name': region_name,
285 'use_dualstack_endpoint': use_dualstack_endpoint,
286 'use_fips_endpoint': use_fips_endpoint,
287 }
289 # Attempt to resolve the exact region for this partition.
290 if region_name in service_data['endpoints']:
291 return self._resolve(**resolve_kwargs)
293 # Check to see if the endpoint provided is valid for the partition.
294 if self._region_match(partition, region_name) or force_partition:
295 # Use the partition endpoint if set and not regionalized.
296 partition_endpoint = service_data.get('partitionEndpoint')
297 is_regionalized = service_data.get('isRegionalized', True)
298 if partition_endpoint and not is_regionalized:
299 LOG.debug(
300 'Using partition endpoint for %s, %s: %s',
301 service_name,
302 region_name,
303 partition_endpoint,
304 )
305 resolve_kwargs['endpoint_name'] = partition_endpoint
306 return self._resolve(**resolve_kwargs)
307 LOG.debug(
308 'Creating a regex based endpoint for %s, %s',
309 service_name,
310 region_name,
311 )
312 return self._resolve(**resolve_kwargs)
314 def _region_match(self, partition, region_name):
315 if region_name in partition['regions']:
316 return True
317 if 'regionRegex' in partition:
318 return re.compile(partition['regionRegex']).match(region_name)
319 return False
321 def _retrieve_variant_data(self, endpoint_data, tags):
322 variants = endpoint_data.get('variants', [])
323 for variant in variants:
324 if set(variant['tags']) == set(tags):
325 result = variant.copy()
326 return result
328 def _create_tag_list(self, use_dualstack_endpoint, use_fips_endpoint):
329 tags = []
330 if use_dualstack_endpoint:
331 tags.append('dualstack')
332 if use_fips_endpoint:
333 tags.append('fips')
334 return tags
336 def _resolve_variant(
337 self, tags, endpoint_data, service_defaults, partition_defaults
338 ):
339 result = {}
340 for variants in [endpoint_data, service_defaults, partition_defaults]:
341 variant = self._retrieve_variant_data(variants, tags)
342 if variant:
343 self._merge_keys(variant, result)
344 return result
346 def _resolve(
347 self,
348 partition,
349 service_name,
350 service_data,
351 endpoint_name,
352 use_dualstack_endpoint,
353 use_fips_endpoint,
354 ):
355 endpoint_data = service_data.get('endpoints', {}).get(
356 endpoint_name, {}
357 )
359 if endpoint_data.get('deprecated'):
360 LOG.warning(
361 f'Client is configured with the deprecated endpoint: {endpoint_name}'
362 )
364 service_defaults = service_data.get('defaults', {})
365 partition_defaults = partition.get('defaults', {})
366 tags = self._create_tag_list(use_dualstack_endpoint, use_fips_endpoint)
368 if tags:
369 result = self._resolve_variant(
370 tags, endpoint_data, service_defaults, partition_defaults
371 )
372 if result == {}:
373 error_msg = (
374 f"Endpoint does not exist for {service_name} "
375 f"in region {endpoint_name}"
376 )
377 raise EndpointVariantError(tags=tags, error_msg=error_msg)
378 self._merge_keys(endpoint_data, result)
379 else:
380 result = endpoint_data
382 # If dnsSuffix has not already been consumed from a variant definition
383 if 'dnsSuffix' not in result:
384 result['dnsSuffix'] = partition['dnsSuffix']
386 result['partition'] = partition['partition']
387 result['endpointName'] = endpoint_name
389 # Merge in the service defaults then the partition defaults.
390 self._merge_keys(service_defaults, result)
391 self._merge_keys(partition_defaults, result)
393 result['hostname'] = self._expand_template(
394 partition,
395 result['hostname'],
396 service_name,
397 endpoint_name,
398 result['dnsSuffix'],
399 )
400 if 'sslCommonName' in result:
401 result['sslCommonName'] = self._expand_template(
402 partition,
403 result['sslCommonName'],
404 service_name,
405 endpoint_name,
406 result['dnsSuffix'],
407 )
409 return result
411 def _merge_keys(self, from_data, result):
412 for key in from_data:
413 if key not in result:
414 result[key] = from_data[key]
416 def _expand_template(
417 self, partition, template, service_name, endpoint_name, dnsSuffix
418 ):
419 return template.format(
420 service=service_name, region=endpoint_name, dnsSuffix=dnsSuffix
421 )
424class EndpointResolverBuiltins(str, Enum):
425 # The AWS Region configured for the SDK client (str)
426 AWS_REGION = "AWS::Region"
427 # Whether the UseFIPSEndpoint configuration option has been enabled for
428 # the SDK client (bool)
429 AWS_USE_FIPS = "AWS::UseFIPS"
430 # Whether the UseDualStackEndpoint configuration option has been enabled
431 # for the SDK client (bool)
432 AWS_USE_DUALSTACK = "AWS::UseDualStack"
433 # Whether the global endpoint should be used with STS, rather the the
434 # regional endpoint for us-east-1 (bool)
435 AWS_STS_USE_GLOBAL_ENDPOINT = "AWS::STS::UseGlobalEndpoint"
436 # Whether the global endpoint should be used with S3, rather then the
437 # regional endpoint for us-east-1 (bool)
438 AWS_S3_USE_GLOBAL_ENDPOINT = "AWS::S3::UseGlobalEndpoint"
439 # Whether S3 Transfer Acceleration has been requested (bool)
440 AWS_S3_ACCELERATE = "AWS::S3::Accelerate"
441 # Whether S3 Force Path Style has been enabled (bool)
442 AWS_S3_FORCE_PATH_STYLE = "AWS::S3::ForcePathStyle"
443 # Whether to use the ARN region or raise an error when ARN and client
444 # region differ (for s3 service only, bool)
445 AWS_S3_USE_ARN_REGION = "AWS::S3::UseArnRegion"
446 # Whether to use the ARN region or raise an error when ARN and client
447 # region differ (for s3-control service only, bool)
448 AWS_S3CONTROL_USE_ARN_REGION = 'AWS::S3Control::UseArnRegion'
449 # Whether multi-region access points (MRAP) should be disabled (bool)
450 AWS_S3_DISABLE_MRAP = "AWS::S3::DisableMultiRegionAccessPoints"
451 # Whether a custom endpoint has been configured (str)
452 SDK_ENDPOINT = "SDK::Endpoint"
453 # Currently not implemented:
454 ACCOUNT_ID = "AWS::Auth::AccountId"
455 ACCOUNT_ID_ENDPOINT_MODE = "AWS::Auth::AccountIdEndpointMode"
458class EndpointRulesetResolver:
459 """Resolves endpoints using a service's endpoint ruleset"""
461 def __init__(
462 self,
463 endpoint_ruleset_data,
464 partition_data,
465 service_model,
466 builtins,
467 client_context,
468 event_emitter,
469 use_ssl=True,
470 requested_auth_scheme=None,
471 ):
472 self._provider = EndpointProvider(
473 ruleset_data=endpoint_ruleset_data,
474 partition_data=partition_data,
475 )
476 self._param_definitions = self._provider.ruleset.parameters
477 self._service_model = service_model
478 self._builtins = builtins
479 self._client_context = client_context
480 self._event_emitter = event_emitter
481 self._use_ssl = use_ssl
482 self._requested_auth_scheme = requested_auth_scheme
483 self._instance_cache = {}
485 def construct_endpoint(
486 self,
487 operation_model,
488 call_args,
489 request_context,
490 ):
491 """Invokes the provider with params defined in the service's ruleset"""
492 if call_args is None:
493 call_args = {}
495 if request_context is None:
496 request_context = {}
498 provider_params = self._get_provider_params(
499 operation_model, call_args, request_context
500 )
501 LOG.debug(
502 f'Calling endpoint provider with parameters: {provider_params}'
503 )
504 try:
505 provider_result = self._provider.resolve_endpoint(
506 **provider_params
507 )
508 except EndpointProviderError as ex:
509 botocore_exception = self.ruleset_error_to_botocore_exception(
510 ex, provider_params
511 )
512 if botocore_exception is None:
513 raise
514 else:
515 raise botocore_exception from ex
516 LOG.debug(f'Endpoint provider result: {provider_result.url}')
518 # The endpoint provider does not support non-secure transport.
519 if not self._use_ssl and provider_result.url.startswith('https://'):
520 provider_result = provider_result._replace(
521 url=f'http://{provider_result.url[8:]}'
522 )
524 # Multi-valued headers are not supported in botocore. Replace the list
525 # of values returned for each header with just its first entry,
526 # dropping any additionally entries.
527 provider_result = provider_result._replace(
528 headers={
529 key: val[0] for key, val in provider_result.headers.items()
530 }
531 )
533 return provider_result
535 def _get_provider_params(
536 self, operation_model, call_args, request_context
537 ):
538 """Resolve a value for each parameter defined in the service's ruleset
540 The resolution order for parameter values is:
541 1. Operation-specific static context values from the service definition
542 2. Operation-specific dynamic context values from API parameters
543 3. Client-specific context parameters
544 4. Built-in values such as region, FIPS usage, ...
545 """
546 provider_params = {}
547 # Builtin values can be customized for each operation by hooks
548 # subscribing to the ``before-endpoint-resolution.*`` event.
549 customized_builtins = self._get_customized_builtins(
550 operation_model, call_args, request_context
551 )
552 for param_name, param_def in self._param_definitions.items():
553 param_val = self._resolve_param_from_context(
554 param_name=param_name,
555 operation_model=operation_model,
556 call_args=call_args,
557 )
558 if param_val is None and param_def.builtin is not None:
559 param_val = self._resolve_param_as_builtin(
560 builtin_name=param_def.builtin,
561 builtins=customized_builtins,
562 )
563 if param_val is not None:
564 provider_params[param_name] = param_val
566 return provider_params
568 def _resolve_param_from_context(
569 self, param_name, operation_model, call_args
570 ):
571 static = self._resolve_param_as_static_context_param(
572 param_name, operation_model
573 )
574 if static is not None:
575 return static
576 dynamic = self._resolve_param_as_dynamic_context_param(
577 param_name, operation_model, call_args
578 )
579 if dynamic is not None:
580 return dynamic
581 return self._resolve_param_as_client_context_param(param_name)
583 def _resolve_param_as_static_context_param(
584 self, param_name, operation_model
585 ):
586 static_ctx_params = self._get_static_context_params(operation_model)
587 return static_ctx_params.get(param_name)
589 def _resolve_param_as_dynamic_context_param(
590 self, param_name, operation_model, call_args
591 ):
592 dynamic_ctx_params = self._get_dynamic_context_params(operation_model)
593 if param_name in dynamic_ctx_params:
594 member_name = dynamic_ctx_params[param_name]
595 return call_args.get(member_name)
597 def _resolve_param_as_client_context_param(self, param_name):
598 client_ctx_params = self._get_client_context_params()
599 if param_name in client_ctx_params:
600 client_ctx_varname = client_ctx_params[param_name]
601 return self._client_context.get(client_ctx_varname)
603 def _resolve_param_as_builtin(self, builtin_name, builtins):
604 if builtin_name not in EndpointResolverBuiltins.__members__.values():
605 raise UnknownEndpointResolutionBuiltInName(name=builtin_name)
606 return builtins.get(builtin_name)
608 @instance_cache
609 def _get_static_context_params(self, operation_model):
610 """Mapping of param names to static param value for an operation"""
611 return {
612 param.name: param.value
613 for param in operation_model.static_context_parameters
614 }
616 @instance_cache
617 def _get_dynamic_context_params(self, operation_model):
618 """Mapping of param names to member names for an operation"""
619 return {
620 param.name: param.member_name
621 for param in operation_model.context_parameters
622 }
624 @instance_cache
625 def _get_client_context_params(self):
626 """Mapping of param names to client configuration variable"""
627 return {
628 param.name: xform_name(param.name)
629 for param in self._service_model.client_context_parameters
630 }
632 def _get_customized_builtins(
633 self, operation_model, call_args, request_context
634 ):
635 service_id = self._service_model.service_id.hyphenize()
636 customized_builtins = copy.copy(self._builtins)
637 # Handlers are expected to modify the builtins dict in place.
638 self._event_emitter.emit(
639 f'before-endpoint-resolution.{service_id}',
640 builtins=customized_builtins,
641 model=operation_model,
642 params=call_args,
643 context=request_context,
644 )
645 return customized_builtins
647 def auth_schemes_to_signing_ctx(self, auth_schemes):
648 """Convert an Endpoint's authSchemes property to a signing_context dict
650 :type auth_schemes: list
651 :param auth_schemes: A list of dictionaries taken from the
652 ``authSchemes`` property of an Endpoint object returned by
653 ``EndpointProvider``.
655 :rtype: str, dict
656 :return: Tuple of auth type string (to be used in
657 ``request_context['auth_type']``) and signing context dict (for use
658 in ``request_context['signing']``).
659 """
660 if not isinstance(auth_schemes, list) or len(auth_schemes) == 0:
661 raise TypeError("auth_schemes must be a non-empty list.")
663 LOG.debug(
664 'Selecting from endpoint provider\'s list of auth schemes: %s. '
665 'User selected auth scheme is: "%s"',
666 ', '.join([f'"{s.get("name")}"' for s in auth_schemes]),
667 self._requested_auth_scheme,
668 )
670 if self._requested_auth_scheme == UNSIGNED:
671 return 'none', {}
673 auth_schemes = [
674 {**scheme, 'name': self._strip_sig_prefix(scheme['name'])}
675 for scheme in auth_schemes
676 ]
677 if self._requested_auth_scheme is not None:
678 try:
679 # Use the first scheme that matches the requested scheme,
680 # after accounting for naming differences between botocore and
681 # endpoint rulesets. Keep the requested name.
682 name, scheme = next(
683 (self._requested_auth_scheme, s)
684 for s in auth_schemes
685 if self._does_botocore_authname_match_ruleset_authname(
686 self._requested_auth_scheme, s['name']
687 )
688 )
689 except StopIteration:
690 # For legacy signers, no match will be found. Do not raise an
691 # exception, instead default to the logic in botocore
692 # customizations.
693 return None, {}
694 else:
695 try:
696 name, scheme = next(
697 (s['name'], s)
698 for s in auth_schemes
699 if s['name'] in AUTH_TYPE_MAPS
700 )
701 except StopIteration:
702 # If no auth scheme was specifically requested and an
703 # authSchemes list is present in the Endpoint object but none
704 # of the entries are supported, raise an exception.
705 fixable_with_crt = False
706 auth_type_options = [s['name'] for s in auth_schemes]
707 if not HAS_CRT:
708 fixable_with_crt = any(
709 scheme in CRT_SUPPORTED_AUTH_TYPES
710 for scheme in auth_type_options
711 )
713 if fixable_with_crt:
714 raise MissingDependencyException(
715 msg='This operation requires an additional dependency.'
716 ' Use pip install botocore[crt] before proceeding.'
717 )
718 else:
719 raise UnknownSignatureVersionError(
720 signature_version=', '.join(auth_type_options)
721 )
723 signing_context = {}
724 if 'signingRegion' in scheme:
725 signing_context['region'] = scheme['signingRegion']
726 elif 'signingRegionSet' in scheme:
727 if len(scheme['signingRegionSet']) > 0:
728 signing_context['region'] = ','.join(
729 scheme['signingRegionSet']
730 )
731 if 'signingName' in scheme:
732 signing_context.update(signing_name=scheme['signingName'])
733 if 'disableDoubleEncoding' in scheme:
734 signing_context['disableDoubleEncoding'] = ensure_boolean(
735 scheme['disableDoubleEncoding']
736 )
738 LOG.debug(
739 'Selected auth type "%s" as "%s" with signing context params: %s',
740 scheme['name'], # original name without "sig"
741 name, # chosen name can differ when `signature_version` is set
742 signing_context,
743 )
744 return name, signing_context
746 def _strip_sig_prefix(self, auth_name):
747 """Normalize auth type names by removing any "sig" prefix"""
748 return auth_name[3:] if auth_name.startswith('sig') else auth_name
750 def _does_botocore_authname_match_ruleset_authname(self, botoname, rsname):
751 """
752 Whether a valid string provided as signature_version parameter for
753 client construction refers to the same auth methods as a string
754 returned by the endpoint ruleset provider. This accounts for:
756 * The ruleset prefixes auth names with "sig"
757 * The s3 and s3control rulesets don't distinguish between v4[a] and
758 s3v4[a] signers
759 * The v2, v3, and HMAC v1 based signers (s3, s3-*) are botocore legacy
760 features and do not exist in the rulesets
761 * Only characters up to the first dash are considered
763 Example matches:
764 * v4, sigv4
765 * v4, v4
766 * s3v4, sigv4
767 * s3v7, sigv7 (hypothetical example)
768 * s3v4a, sigv4a
769 * s3v4-query, sigv4
771 Example mismatches:
772 * v4a, sigv4
773 * s3, sigv4
774 * s3-presign-post, sigv4
775 """
776 rsname = self._strip_sig_prefix(rsname)
777 botoname = botoname.split('-')[0]
778 if botoname != 's3' and botoname.startswith('s3'):
779 botoname = botoname[2:]
780 return rsname == botoname
782 def ruleset_error_to_botocore_exception(self, ruleset_exception, params):
783 """Attempts to translate ruleset errors to pre-existing botocore
784 exception types by string matching exception strings.
785 """
786 msg = ruleset_exception.kwargs.get('msg')
787 if msg is None:
788 return
790 if msg.startswith('Invalid region in ARN: '):
791 # Example message:
792 # "Invalid region in ARN: `us-we$t-2` (invalid DNS name)"
793 try:
794 label = msg.split('`')[1]
795 except IndexError:
796 label = msg
797 return InvalidHostLabelError(label=label)
799 service_name = self._service_model.service_name
800 if service_name == 's3':
801 if (
802 msg == 'S3 Object Lambda does not support S3 Accelerate'
803 or msg == 'Accelerate cannot be used with FIPS'
804 ):
805 return UnsupportedS3ConfigurationError(msg=msg)
806 if (
807 msg.startswith('S3 Outposts does not support')
808 or msg.startswith('S3 MRAP does not support')
809 or msg.startswith('S3 Object Lambda does not support')
810 or msg.startswith('Access Points do not support')
811 or msg.startswith('Invalid configuration:')
812 or msg.startswith('Client was configured for partition')
813 ):
814 return UnsupportedS3AccesspointConfigurationError(msg=msg)
815 if msg.lower().startswith('invalid arn:'):
816 return ParamValidationError(report=msg)
817 if service_name == 's3control':
818 if msg.startswith('Invalid ARN:'):
819 arn = params.get('Bucket')
820 return UnsupportedS3ControlArnError(arn=arn, msg=msg)
821 if msg.startswith('Invalid configuration:') or msg.startswith(
822 'Client was configured for partition'
823 ):
824 return UnsupportedS3ControlConfigurationError(msg=msg)
825 if msg == "AccountId is required but not set":
826 return ParamValidationError(report=msg)
827 if service_name == 'events':
828 if msg.startswith(
829 'Invalid Configuration: FIPS is not supported with '
830 'EventBridge multi-region endpoints.'
831 ):
832 return InvalidEndpointConfigurationError(msg=msg)
833 if msg == 'EndpointId must be a valid host label.':
834 return InvalidEndpointConfigurationError(msg=msg)
835 return None