Skip to content
Open
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
47 changes: 44 additions & 3 deletions botocore/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
UnknownCredentialError,
)
from botocore.tokens import SSOTokenProvider
from botocore.useragent import register_feature_id
from botocore.useragent import register_feature_id, register_feature_ids
from botocore.utils import (
ArnParser,
ContainerMetadataFetcher,
Expand Down Expand Up @@ -705,6 +705,15 @@ def __init__(self, cache=None, expiry_window_seconds=None):
if expiry_window_seconds is None:
expiry_window_seconds = self.DEFAULT_EXPIRY_WINDOW_SECONDS
self._expiry_window_seconds = expiry_window_seconds
self._feature_ids = set()

@property
def feature_ids(self):
return self._feature_ids

@feature_ids.setter
def feature_ids(self, value):
self._feature_ids = value

def _create_cache_key(self):
raise NotImplementedError('_create_cache_key()')
Expand Down Expand Up @@ -885,6 +894,7 @@ def __init__(

def _get_credentials(self):
"""Get credentials by calling assume role."""
register_feature_ids(self._feature_ids)
kwargs = self._assume_role_kwargs()
client = self._create_client()
response = client.assume_role(**kwargs)
Expand Down Expand Up @@ -971,6 +981,7 @@ def __init__(

def _get_credentials(self):
"""Get credentials by calling assume role."""
register_feature_ids(self._feature_ids)
kwargs = self._assume_role_kwargs()
# Assume role with web identity does not require credentials other than
# the token, explicitly configure the client to not sign requests.
Expand Down Expand Up @@ -1365,6 +1376,7 @@ def load(self):
)
token = self._get_session_token(config)
account_id = self._get_account_id(config)
register_feature_id('CREDENTIALS_PROFILE')
return Credentials(
access_key,
secret_key,
Expand Down Expand Up @@ -1432,6 +1444,7 @@ def load(self):
)
token = self._get_session_token(profile_config)
account_id = self._get_account_id(profile_config)
register_feature_id('CREDENTIALS_PROFILE')
return Credentials(
access_key,
secret_key,
Expand Down Expand Up @@ -1510,6 +1523,11 @@ class AssumeRoleProvider(CredentialProvider):
# remaining time left until the credentials expires is less than the
# EXPIRY_WINDOW.
EXPIRY_WINDOW_SECONDS = 60 * 15
NAMED_PROVIDER_FEATURE_MAP = {
'Ec2InstanceMetadata': 'CREDENTIALS_IMDS',
'Environment': 'CREDENTIALS_ENV_VARS',
'EcsContainer': 'CREDENTIALS_HTTP',
}

def __init__(
self,
Expand Down Expand Up @@ -1572,6 +1590,7 @@ def __init__(
self._credential_sourcer = credential_sourcer
self._profile_provider_builder = profile_provider_builder
self._visited_profiles = [self._profile_name]
self._feature_ids = set()

def load(self):
self._loaded_config = self._load_config()
Expand Down Expand Up @@ -1622,10 +1641,13 @@ def _load_creds_via_assume_role(self, profile_name):
mfa_prompter=self._prompter,
cache=self.cache,
)
fetcher.feature_ids = self._feature_ids.copy()
refresher = fetcher.fetch_credentials
if mfa_serial is not None:
refresher = create_mfa_serial_refresher(refresher)

self._feature_ids.add('CREDENTIALS_STS_ASSUME_ROLE')
register_feature_ids(self._feature_ids)
# The initial credentials are empty and the expiration time is set
# to now so that we can delay the call to assume role until it is
# strictly needed.
Expand Down Expand Up @@ -1754,18 +1776,20 @@ def _has_static_credentials(self, profile):
def _resolve_source_credentials(self, role_config, profile_name):
credential_source = role_config.get('credential_source')
if credential_source is not None:
self._feature_ids.add('CREDENTIALS_PROFILE_NAMED_PROVIDER')
return self._resolve_credentials_from_source(
credential_source, profile_name
)

source_profile = role_config['source_profile']
self._visited_profiles.append(source_profile)
self._feature_ids.add('CREDENTIALS_PROFILE_SOURCE_PROFILE')
return self._resolve_credentials_from_profile(source_profile)

def _resolve_credentials_from_profile(self, profile_name):
profiles = self._loaded_config.get('profiles', {})
profile = profiles[profile_name]

self._feature_ids.add('CREDENTIALS_PROFILE')
if (
self._has_static_credentials(profile)
and not self._profile_provider_builder
Expand Down Expand Up @@ -1821,6 +1845,11 @@ def _resolve_credentials_from_source(
f'in profile {profile_name}'
),
)
named_provider_feature_id = self.NAMED_PROVIDER_FEATURE_MAP.get(
credential_source
)
if named_provider_feature_id:
self._feature_ids.add(named_provider_feature_id)
return credentials


Expand Down Expand Up @@ -1851,6 +1880,7 @@ def __init__(
if token_loader_cls is None:
token_loader_cls = FileWebIdentityTokenLoader
self._token_loader_cls = token_loader_cls
self._feature_ids = set()

def load(self):
return self._assume_role_with_web_identity()
Expand All @@ -1873,8 +1903,15 @@ def _get_env_config(self, key):
def _get_config(self, key):
env_value = self._get_env_config(key)
if env_value is not None:
self._feature_ids.add('CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN')
return env_value
return self._get_profile_config(key)

config_value = self._get_profile_config(key)
if config_value is not None:
self._feature_ids.add('CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN')
return config_value

return None

def _assume_role_with_web_identity(self):
token_path = self._get_config('web_identity_token_file')
Expand Down Expand Up @@ -1904,6 +1941,10 @@ def _assume_role_with_web_identity(self):
extra_args=extra_args,
cache=self.cache,
)
fetcher.feature_ids = self._feature_ids.copy()

self._feature_ids.add('CREDENTIALS_STS_ASSUME_ROLE_WEB_ID')
register_feature_ids(self._feature_ids)
# The initial credentials are empty and the expiration time is set
# to now so that we can delay the call to assume role until it is
# strictly needed.
Expand Down
18 changes: 18 additions & 0 deletions botocore/useragent.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@
'FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED': 'c',
'CREDENTIALS_CODE': 'e',
'CREDENTIALS_ENV_VARS': 'g',
'CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN': 'h',
'CREDENTIALS_STS_ASSUME_ROLE': 'i',
'CREDENTIALS_STS_ASSUME_ROLE_WEB_ID': 'k',
'CREDENTIALS_PROFILE': 'n',
'CREDENTIALS_PROFILE_SOURCE_PROFILE': 'o',
'CREDENTIALS_PROFILE_NAMED_PROVIDER': 'p',
'CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN': 'q',
'CREDENTIALS_HTTP': 'z',
'CREDENTIALS_IMDS': '0',
'BEARER_SERVICE_ENV_VARS': '3',
Expand All @@ -105,6 +112,17 @@ def register_feature_id(feature_id):
ctx.features.add(val)


def register_feature_ids(feature_ids):
"""Adds multiple feature IDs to the current context object's ``features`` set.

:type feature_ids: iterable of str
:param feature_ids: An iterable of feature ID strings to register. Each
value must be a key in the ``_USERAGENT_FEATURE_MAPPINGS`` dict.
"""
for feature_id in feature_ids:
register_feature_id(feature_id)


def sanitize_user_agent_string_component(raw_str, allow_hash):
"""Replaces all not allowed characters in the string with a dash ("-").

Expand Down
Loading
Loading