Skip to content

Commit 937d8ae

Browse files
committed
Merge branch 'immuta-develop' into develop
PR boto#1717. * immuta-develop: Add missing test cases for empty expiry values Add changelog entry Simplify empty env var creds check If any auth env var is empty string, treat it as if it was unset
2 parents fa72dd2 + c9bc7fb commit 937d8ae

File tree

3 files changed

+78
-14
lines changed

3 files changed

+78
-14
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"category": "Environment Variables",
3+
"type": "enhancement",
4+
"description": "Ignore env var credentials is values are empty (`#1680 <https://github.com/boto/botocore/issues/1680>`__)"
5+
}

botocore/credentials.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,10 @@ def load(self):
921921
"""
922922
Search for credentials in explicit environment variables.
923923
"""
924-
if self._mapping['access_key'] in self.environ:
924+
925+
access_key = self.environ.get(self._mapping['access_key'], '')
926+
927+
if access_key:
925928
logger.info('Found credentials in environment variables.')
926929
fetcher = self._create_credentials_fetcher()
927930
credentials = fetcher(require_expiry=False)
@@ -950,30 +953,32 @@ def _create_credentials_fetcher(self):
950953
def fetch_credentials(require_expiry=True):
951954
credentials = {}
952955

953-
access_key = environ.get(mapping['access_key'])
954-
if access_key is None:
956+
access_key = environ.get(mapping['access_key'], '')
957+
if not access_key:
955958
raise PartialCredentialsError(
956959
provider=method, cred_var=mapping['access_key'])
957960
credentials['access_key'] = access_key
958961

959-
secret_key = environ.get(mapping['secret_key'])
960-
if secret_key is None:
962+
secret_key = environ.get(mapping['secret_key'], '')
963+
if not secret_key:
961964
raise PartialCredentialsError(
962965
provider=method, cred_var=mapping['secret_key'])
963966
credentials['secret_key'] = secret_key
964967

965-
token = None
968+
credentials['token'] = None
966969
for token_env_var in mapping['token']:
967-
if token_env_var in environ:
968-
token = environ[token_env_var]
970+
token = environ.get(token_env_var, '')
971+
if token:
972+
credentials['token'] = token
969973
break
970-
credentials['token'] = token
971974

972-
expiry_time = environ.get(mapping['expiry_time'])
973-
if require_expiry and expiry_time is None:
975+
credentials['expiry_time'] = None
976+
expiry_time = environ.get(mapping['expiry_time'], '')
977+
if expiry_time:
978+
credentials['expiry_time'] = expiry_time
979+
if require_expiry and not expiry_time:
974980
raise PartialCredentialsError(
975981
provider=method, cred_var=mapping['expiry_time'])
976-
credentials['expiry_time'] = expiry_time
977982

978983
return credentials
979984

tests/unit/test_credentials.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,48 @@ def test_envvars_not_found(self):
691691
creds = provider.load()
692692
self.assertIsNone(creds)
693693

694+
def test_envvars_empty_string(self):
695+
environ = {
696+
'AWS_ACCESS_KEY_ID': '',
697+
'AWS_SECRET_ACCESS_KEY': '',
698+
'AWS_SECURITY_TOKEN': '',
699+
}
700+
provider = credentials.EnvProvider(environ)
701+
creds = provider.load()
702+
self.assertIsNone(creds)
703+
704+
def test_expiry_omitted_if_envvar_empty(self):
705+
environ = {
706+
'AWS_ACCESS_KEY_ID': 'foo',
707+
'AWS_SECRET_ACCESS_KEY': 'bar',
708+
'AWS_SESSION_TOKEN': 'baz',
709+
'AWS_CREDENTIAL_EXPIRATION': '',
710+
}
711+
provider = credentials.EnvProvider(environ)
712+
creds = provider.load()
713+
# Because we treat empty env vars the same as not being provided,
714+
# we should return static credentials and not a refreshable
715+
# credential.
716+
self.assertNotIsInstance(creds, credentials.RefreshableCredentials)
717+
self.assertEqual(creds.access_key, 'foo')
718+
self.assertEqual(creds.secret_key, 'bar')
719+
self.assertEqual(creds.token, 'baz')
720+
721+
def test_error_when_expiry_required_but_empty(self):
722+
expiry_time = datetime.now(tzlocal()) - timedelta(hours=1)
723+
environ = {
724+
'AWS_ACCESS_KEY_ID': 'foo',
725+
'AWS_SECRET_ACCESS_KEY': 'bar',
726+
'AWS_CREDENTIAL_EXPIRATION': expiry_time.isoformat(),
727+
}
728+
provider = credentials.EnvProvider(environ)
729+
creds = provider.load()
730+
731+
del environ['AWS_CREDENTIAL_EXPIRATION']
732+
733+
with self.assertRaises(botocore.exceptions.PartialCredentialsError):
734+
creds.get_frozen_credentials()
735+
694736
def test_can_override_env_var_mapping(self):
695737
# We can change the env var provider to
696738
# use our specified env var names.
@@ -765,6 +807,18 @@ def test_partial_creds_is_an_error(self):
765807
with self.assertRaises(botocore.exceptions.PartialCredentialsError):
766808
provider.load()
767809

810+
def test_partial_creds_is_an_error_empty_string(self):
811+
# If the user provides an access key, they must also
812+
# provide a secret key. Not doing so will generate an
813+
# error.
814+
environ = {
815+
'AWS_ACCESS_KEY_ID': 'foo',
816+
'AWS_SECRET_ACCESS_KEY': '',
817+
}
818+
provider = credentials.EnvProvider(environ)
819+
with self.assertRaises(botocore.exceptions.PartialCredentialsError):
820+
provider.load()
821+
768822
def test_missing_access_key_id_raises_error(self):
769823
expiry_time = datetime.now(tzlocal()) - timedelta(hours=1)
770824
environ = {
@@ -1823,7 +1877,7 @@ def test_external_id_provided(self):
18231877
RoleArn='myrole', ExternalId='myid', RoleSessionName=mock.ANY)
18241878

18251879
def test_assume_role_with_duration(self):
1826-
self.fake_config['profiles']['development']['duration_seconds'] = 7200
1880+
self.fake_config['profiles']['development']['duration_seconds'] = 7200
18271881
response = {
18281882
'Credentials': {
18291883
'AccessKeyId': 'foo',
@@ -1842,7 +1896,7 @@ def test_assume_role_with_duration(self):
18421896

18431897
client = client_creator.return_value
18441898
client.assume_role.assert_called_with(
1845-
RoleArn='myrole', RoleSessionName=mock.ANY,
1899+
RoleArn='myrole', RoleSessionName=mock.ANY,
18461900
DurationSeconds=7200)
18471901

18481902
def test_assume_role_with_bad_duration(self):

0 commit comments

Comments
 (0)