Skip to content

Commit 8455ff9

Browse files
authored
Track assume-role and profile related credential feature ids (#3554)
1 parent f9f02dc commit 8455ff9

File tree

4 files changed

+345
-3
lines changed

4 files changed

+345
-3
lines changed

botocore/credentials.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
UnknownCredentialError,
4545
)
4646
from botocore.tokens import SSOTokenProvider
47-
from botocore.useragent import register_feature_id
47+
from botocore.useragent import register_feature_id, register_feature_ids
4848
from botocore.utils import (
4949
ArnParser,
5050
ContainerMetadataFetcher,
@@ -705,6 +705,7 @@ def __init__(self, cache=None, expiry_window_seconds=None):
705705
if expiry_window_seconds is None:
706706
expiry_window_seconds = self.DEFAULT_EXPIRY_WINDOW_SECONDS
707707
self._expiry_window_seconds = expiry_window_seconds
708+
self.feature_ids = set()
708709

709710
def _create_cache_key(self):
710711
raise NotImplementedError('_create_cache_key()')
@@ -885,6 +886,7 @@ def __init__(
885886

886887
def _get_credentials(self):
887888
"""Get credentials by calling assume role."""
889+
register_feature_ids(self.feature_ids)
888890
kwargs = self._assume_role_kwargs()
889891
client = self._create_client()
890892
response = client.assume_role(**kwargs)
@@ -971,6 +973,7 @@ def __init__(
971973

972974
def _get_credentials(self):
973975
"""Get credentials by calling assume role."""
976+
register_feature_ids(self.feature_ids)
974977
kwargs = self._assume_role_kwargs()
975978
# Assume role with web identity does not require credentials other than
976979
# the token, explicitly configure the client to not sign requests.
@@ -1367,6 +1370,7 @@ def load(self):
13671370
)
13681371
token = self._get_session_token(config)
13691372
account_id = self._get_account_id(config)
1373+
register_feature_id('CREDENTIALS_PROFILE')
13701374
return Credentials(
13711375
access_key,
13721376
secret_key,
@@ -1434,6 +1438,7 @@ def load(self):
14341438
)
14351439
token = self._get_session_token(profile_config)
14361440
account_id = self._get_account_id(profile_config)
1441+
register_feature_id('CREDENTIALS_PROFILE')
14371442
return Credentials(
14381443
access_key,
14391444
secret_key,
@@ -1513,6 +1518,11 @@ class AssumeRoleProvider(CredentialProvider):
15131518
# remaining time left until the credentials expires is less than the
15141519
# EXPIRY_WINDOW.
15151520
EXPIRY_WINDOW_SECONDS = 60 * 15
1521+
NAMED_PROVIDER_FEATURE_MAP = {
1522+
'Ec2InstanceMetadata': 'CREDENTIALS_IMDS',
1523+
'Environment': 'CREDENTIALS_ENV_VARS',
1524+
'EcsContainer': 'CREDENTIALS_HTTP',
1525+
}
15161526

15171527
def __init__(
15181528
self,
@@ -1575,6 +1585,7 @@ def __init__(
15751585
self._credential_sourcer = credential_sourcer
15761586
self._profile_provider_builder = profile_provider_builder
15771587
self._visited_profiles = [self._profile_name]
1588+
self._feature_ids = set()
15781589

15791590
def load(self):
15801591
self._loaded_config = self._load_config()
@@ -1625,10 +1636,13 @@ def _load_creds_via_assume_role(self, profile_name):
16251636
mfa_prompter=self._prompter,
16261637
cache=self.cache,
16271638
)
1639+
fetcher.feature_ids = self._feature_ids.copy()
16281640
refresher = fetcher.fetch_credentials
16291641
if mfa_serial is not None:
16301642
refresher = create_mfa_serial_refresher(refresher)
16311643

1644+
self._feature_ids.add('CREDENTIALS_STS_ASSUME_ROLE')
1645+
register_feature_ids(self._feature_ids)
16321646
# The initial credentials are empty and the expiration time is set
16331647
# to now so that we can delay the call to assume role until it is
16341648
# strictly needed.
@@ -1757,18 +1771,20 @@ def _has_static_credentials(self, profile):
17571771
def _resolve_source_credentials(self, role_config, profile_name):
17581772
credential_source = role_config.get('credential_source')
17591773
if credential_source is not None:
1774+
self._feature_ids.add('CREDENTIALS_PROFILE_NAMED_PROVIDER')
17601775
return self._resolve_credentials_from_source(
17611776
credential_source, profile_name
17621777
)
17631778

17641779
source_profile = role_config['source_profile']
17651780
self._visited_profiles.append(source_profile)
1781+
self._feature_ids.add('CREDENTIALS_PROFILE_SOURCE_PROFILE')
17661782
return self._resolve_credentials_from_profile(source_profile)
17671783

17681784
def _resolve_credentials_from_profile(self, profile_name):
17691785
profiles = self._loaded_config.get('profiles', {})
17701786
profile = profiles[profile_name]
1771-
1787+
self._feature_ids.add('CREDENTIALS_PROFILE')
17721788
if (
17731789
self._has_static_credentials(profile)
17741790
and not self._profile_provider_builder
@@ -1824,6 +1840,11 @@ def _resolve_credentials_from_source(
18241840
f'in profile {profile_name}'
18251841
),
18261842
)
1843+
named_provider_feature_id = self.NAMED_PROVIDER_FEATURE_MAP.get(
1844+
credential_source
1845+
)
1846+
if named_provider_feature_id:
1847+
self._feature_ids.add(named_provider_feature_id)
18271848
return credentials
18281849

18291850

@@ -1854,6 +1875,7 @@ def __init__(
18541875
if token_loader_cls is None:
18551876
token_loader_cls = FileWebIdentityTokenLoader
18561877
self._token_loader_cls = token_loader_cls
1878+
self._feature_ids = set()
18571879

18581880
def load(self):
18591881
return self._assume_role_with_web_identity()
@@ -1876,8 +1898,15 @@ def _get_env_config(self, key):
18761898
def _get_config(self, key):
18771899
env_value = self._get_env_config(key)
18781900
if env_value is not None:
1901+
self._feature_ids.add('CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN')
18791902
return env_value
1880-
return self._get_profile_config(key)
1903+
1904+
config_value = self._get_profile_config(key)
1905+
if config_value is not None:
1906+
self._feature_ids.add('CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN')
1907+
return config_value
1908+
1909+
return None
18811910

18821911
def _assume_role_with_web_identity(self):
18831912
token_path = self._get_config('web_identity_token_file')
@@ -1907,6 +1936,10 @@ def _assume_role_with_web_identity(self):
19071936
extra_args=extra_args,
19081937
cache=self.cache,
19091938
)
1939+
fetcher.feature_ids = self._feature_ids.copy()
1940+
1941+
self._feature_ids.add('CREDENTIALS_STS_ASSUME_ROLE_WEB_ID')
1942+
register_feature_ids(self._feature_ids)
19101943
# The initial credentials are empty and the expiration time is set
19111944
# to now so that we can delay the call to assume role until it is
19121945
# strictly needed.

botocore/useragent.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@
8080
'FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED': 'c',
8181
'CREDENTIALS_CODE': 'e',
8282
'CREDENTIALS_ENV_VARS': 'g',
83+
'CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN': 'h',
84+
'CREDENTIALS_STS_ASSUME_ROLE': 'i',
85+
'CREDENTIALS_STS_ASSUME_ROLE_WEB_ID': 'k',
86+
'CREDENTIALS_PROFILE': 'n',
87+
'CREDENTIALS_PROFILE_SOURCE_PROFILE': 'o',
88+
'CREDENTIALS_PROFILE_NAMED_PROVIDER': 'p',
89+
'CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN': 'q',
8390
'CREDENTIALS_PROFILE_PROCESS': 'v',
8491
'CREDENTIALS_PROCESS': 'w',
8592
'CREDENTIALS_BOTO2_CONFIG_FILE': 'x',
@@ -108,6 +115,17 @@ def register_feature_id(feature_id):
108115
ctx.features.add(val)
109116

110117

118+
def register_feature_ids(feature_ids):
119+
"""Adds multiple feature IDs to the current context object's ``features`` set.
120+
121+
:type feature_ids: iterable of str
122+
:param feature_ids: An iterable of feature ID strings to register. Each
123+
value must be a key in the ``_USERAGENT_FEATURE_MAPPINGS`` dict.
124+
"""
125+
for feature_id in feature_ids:
126+
register_feature_id(feature_id)
127+
128+
111129
def sanitize_user_agent_string_component(raw_str, allow_hash):
112130
"""Replaces all not allowed characters in the string with a dash ("-").
113131

0 commit comments

Comments
 (0)