Skip to content

Commit

Permalink
Merge branch 'immuta-develop' into develop
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jamesls committed May 14, 2019
2 parents fa72dd2 + c9bc7fb commit 937d8ae
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"category": "Environment Variables",
"type": "enhancement",
"description": "Ignore env var credentials is values are empty (`#1680 <https://github.com/boto/botocore/issues/1680>`__)"
}
29 changes: 17 additions & 12 deletions botocore/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,10 @@ def load(self):
"""
Search for credentials in explicit environment variables.
"""
if self._mapping['access_key'] in self.environ:

access_key = self.environ.get(self._mapping['access_key'], '')

if access_key:
logger.info('Found credentials in environment variables.')
fetcher = self._create_credentials_fetcher()
credentials = fetcher(require_expiry=False)
Expand Down Expand Up @@ -950,30 +953,32 @@ def _create_credentials_fetcher(self):
def fetch_credentials(require_expiry=True):
credentials = {}

access_key = environ.get(mapping['access_key'])
if access_key is None:
access_key = environ.get(mapping['access_key'], '')
if not access_key:
raise PartialCredentialsError(
provider=method, cred_var=mapping['access_key'])
credentials['access_key'] = access_key

secret_key = environ.get(mapping['secret_key'])
if secret_key is None:
secret_key = environ.get(mapping['secret_key'], '')
if not secret_key:
raise PartialCredentialsError(
provider=method, cred_var=mapping['secret_key'])
credentials['secret_key'] = secret_key

token = None
credentials['token'] = None
for token_env_var in mapping['token']:
if token_env_var in environ:
token = environ[token_env_var]
token = environ.get(token_env_var, '')
if token:
credentials['token'] = token
break
credentials['token'] = token

expiry_time = environ.get(mapping['expiry_time'])
if require_expiry and expiry_time is None:
credentials['expiry_time'] = None
expiry_time = environ.get(mapping['expiry_time'], '')
if expiry_time:
credentials['expiry_time'] = expiry_time
if require_expiry and not expiry_time:
raise PartialCredentialsError(
provider=method, cred_var=mapping['expiry_time'])
credentials['expiry_time'] = expiry_time

return credentials

Expand Down
58 changes: 56 additions & 2 deletions tests/unit/test_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,48 @@ def test_envvars_not_found(self):
creds = provider.load()
self.assertIsNone(creds)

def test_envvars_empty_string(self):
environ = {
'AWS_ACCESS_KEY_ID': '',
'AWS_SECRET_ACCESS_KEY': '',
'AWS_SECURITY_TOKEN': '',
}
provider = credentials.EnvProvider(environ)
creds = provider.load()
self.assertIsNone(creds)

def test_expiry_omitted_if_envvar_empty(self):
environ = {
'AWS_ACCESS_KEY_ID': 'foo',
'AWS_SECRET_ACCESS_KEY': 'bar',
'AWS_SESSION_TOKEN': 'baz',
'AWS_CREDENTIAL_EXPIRATION': '',
}
provider = credentials.EnvProvider(environ)
creds = provider.load()
# Because we treat empty env vars the same as not being provided,
# we should return static credentials and not a refreshable
# credential.
self.assertNotIsInstance(creds, credentials.RefreshableCredentials)
self.assertEqual(creds.access_key, 'foo')
self.assertEqual(creds.secret_key, 'bar')
self.assertEqual(creds.token, 'baz')

def test_error_when_expiry_required_but_empty(self):
expiry_time = datetime.now(tzlocal()) - timedelta(hours=1)
environ = {
'AWS_ACCESS_KEY_ID': 'foo',
'AWS_SECRET_ACCESS_KEY': 'bar',
'AWS_CREDENTIAL_EXPIRATION': expiry_time.isoformat(),
}
provider = credentials.EnvProvider(environ)
creds = provider.load()

del environ['AWS_CREDENTIAL_EXPIRATION']

with self.assertRaises(botocore.exceptions.PartialCredentialsError):
creds.get_frozen_credentials()

def test_can_override_env_var_mapping(self):
# We can change the env var provider to
# use our specified env var names.
Expand Down Expand Up @@ -765,6 +807,18 @@ def test_partial_creds_is_an_error(self):
with self.assertRaises(botocore.exceptions.PartialCredentialsError):
provider.load()

def test_partial_creds_is_an_error_empty_string(self):
# If the user provides an access key, they must also
# provide a secret key. Not doing so will generate an
# error.
environ = {
'AWS_ACCESS_KEY_ID': 'foo',
'AWS_SECRET_ACCESS_KEY': '',
}
provider = credentials.EnvProvider(environ)
with self.assertRaises(botocore.exceptions.PartialCredentialsError):
provider.load()

def test_missing_access_key_id_raises_error(self):
expiry_time = datetime.now(tzlocal()) - timedelta(hours=1)
environ = {
Expand Down Expand Up @@ -1823,7 +1877,7 @@ def test_external_id_provided(self):
RoleArn='myrole', ExternalId='myid', RoleSessionName=mock.ANY)

def test_assume_role_with_duration(self):
self.fake_config['profiles']['development']['duration_seconds'] = 7200
self.fake_config['profiles']['development']['duration_seconds'] = 7200
response = {
'Credentials': {
'AccessKeyId': 'foo',
Expand All @@ -1842,7 +1896,7 @@ def test_assume_role_with_duration(self):

client = client_creator.return_value
client.assume_role.assert_called_with(
RoleArn='myrole', RoleSessionName=mock.ANY,
RoleArn='myrole', RoleSessionName=mock.ANY,
DurationSeconds=7200)

def test_assume_role_with_bad_duration(self):
Expand Down

0 comments on commit 937d8ae

Please sign in to comment.