def test_assume_role_with_mfa(self): self.fake_config['profiles']['development']['mfa_serial'] = 'mfa' response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', 'Expiration': datetime.now(tzlocal()).isoformat(), }, } client_creator = self.create_client_creator(with_response=response) prompter = mock.Mock(return_value='token-code') provider = credentials.AssumeRoleProvider(self.create_config_loader(), client_creator, cache={}, profile_name='development', prompter=prompter) provider.load() client = client_creator.return_value # In addition to the normal assume role args, we should also # inject the serial number from the config as well as the # token code that comes from prompting the user (the prompter # object). client.assume_role.assert_called_with(RoleArn='myrole', RoleSessionName=mock.ANY, SerialNumber='mfa', TokenCode='token-code')
def test_cache_key_with_role_session_name(self): response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', 'Expiration': datetime.now(tzlocal()).isoformat() }, } cache = {} self.fake_config['profiles']['development']['role_arn'] = ( 'arn:aws:iam::foo-role') self.fake_config['profiles']['development']['role_session_name'] = ( 'foo_role_session_name') client_creator = self.create_client_creator(with_response=response) provider = credentials.AssumeRoleProvider(self.create_config_loader(), client_creator, cache=cache, profile_name='development') provider.load() self.assertEqual( cache['development--arn_aws_iam__foo-role--foo_role_session_name'], response)
def test_assume_role_in_cache_but_expired(self): expired_creds = datetime.utcnow() valid_creds = expired_creds + timedelta(seconds=60) utc_timestamp = expired_creds.isoformat() + 'Z' response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', 'Expiration': valid_creds.isoformat() + 'Z', }, } client_creator = self.create_client_creator(with_response=response) cache = { 'development--myrole': { 'Credentials': { 'AccessKeyId': 'foo-cached', 'SecretAccessKey': 'bar-cached', 'SessionToken': 'baz-cached', 'Expiration': utc_timestamp, } } } provider = credentials.AssumeRoleProvider(self.create_config_loader(), client_creator, cache=cache, profile_name='development') creds = provider.load() self.assertEqual(creds.access_key, 'foo') self.assertEqual(creds.secret_key, 'bar') self.assertEqual(creds.token, 'baz')
def test_assume_role_with_datetime(self): response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', # Note the lack of isoformat(), we're using # a datetime.datetime type. This will ensure # we test both parsing as well as serializing # from a given datetime because the credentials # are immediately expired. 'Expiration': datetime.now(tzlocal()) + timedelta(hours=20) }, } client_creator = self.create_client_creator(with_response=response) provider = credentials.AssumeRoleProvider(self.create_config_loader(), client_creator, cache={}, profile_name='development') creds = provider.load() self.assertEqual(creds.access_key, 'foo') self.assertEqual(creds.secret_key, 'bar') self.assertEqual(creds.token, 'baz')
def test_assume_role_mfa_cannot_refresh_credentials(self): # Note: we should look into supporting optional behavior # in the future that allows for reprompting for credentials. # But for now, if we get temp creds with MFA then when those # creds expire, we can't refresh the credentials. self.fake_config['profiles']['development']['mfa_serial'] = 'mfa' response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', # We're creating an expiry time in the past so as # soon as we try to access the credentials, the # refresh behavior will be triggered. 'Expiration': ( datetime.now(tzlocal()) - timedelta(seconds=100)).isoformat(), }, } client_creator = self.create_client_creator(with_response=response) provider = credentials.AssumeRoleProvider( self.create_config_loader(), client_creator, cache={}, profile_name='development', prompter=mock.Mock(return_value='token-code')) creds = provider.load() with self.assertRaises(credentials.RefreshWithMFAUnsupportedError): # access_key is a property that will refresh credentials # if they're expired. Because we set the expiry time to # something in the past, this will trigger the refresh # behavior, with with MFA will currently raise an exception. creds.access_key
def test_source_profile_not_provided(self): del self.fake_config['profiles']['development']['source_profile'] provider = credentials.AssumeRoleProvider( self.create_config_loader(), mock.Mock(), cache={}, profile_name='development') # source_profile is required, we shoudl get an error. with self.assertRaises(botocore.exceptions.PartialCredentialsError): provider.load()
def test_source_profile_does_not_exist(self): dev_profile = self.fake_config['profiles']['development'] dev_profile['source_profile'] = 'does-not-exist' provider = credentials.AssumeRoleProvider( self.create_config_loader(), mock.Mock(), cache={}, profile_name='development') # source_profile is required, we shoudl get an error. with self.assertRaises(botocore.exceptions.InvalidConfigError): provider.load()
def test_assume_role_populates_session_name_on_refresh(self): responses = [ { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', # We're creating an expiry time in the past so as # soon as we try to access the credentials, the # refresh behavior will be triggered. 'Expiration': (datetime.now(tzlocal()) - timedelta(seconds=100)).isoformat(), }, }, { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', 'Expiration': (datetime.now(tzlocal()) + timedelta(seconds=100)).isoformat(), } } ] client_creator = self.create_client_creator(with_response=responses) provider = credentials.AssumeRoleProvider( self.create_config_loader(), client_creator, cache={}, profile_name='development', prompter=mock.Mock(return_value='token-code')) # This will trigger the first assume_role() call. It returns # credentials that are expired and will trigger a refresh. creds = provider.load() # This will trigger the second assume_role() call because # a refresh is needed. creds.get_frozen_credentials() client = client_creator.return_value assume_role_calls = client.assume_role.call_args_list self.assertEqual(len(assume_role_calls), 2, assume_role_calls) # The args should be identical. That is, the second # assume_role call should have the exact same args as the # initial assume_role call. self.assertEqual(assume_role_calls[0], assume_role_calls[1])
def test_no_config_is_noop(self): self.fake_config['profiles']['development'] = { 'aws_access_key_id': 'foo', 'aws_secret_access_key': 'bar', } provider = credentials.AssumeRoleProvider( self.create_config_loader(), mock.Mock(), cache={}, profile_name='development') # Because a role_arn was not specified, the AssumeRoleProvider # is a noop and will not return credentials (which means we # move on to the next provider). creds = provider.load() self.assertIsNone(creds)
def test_assume_role_with_no_cache(self): response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', 'Expiration': self.some_future_time().isoformat() }, } client_creator = self.create_client_creator(with_response=response) provider = credentials.AssumeRoleProvider( self.create_config_loader(), client_creator, cache={}, profile_name='development') creds = provider.load() self.assertEqual(creds.access_key, 'foo') self.assertEqual(creds.secret_key, 'bar') self.assertEqual(creds.token, 'baz')
def test_external_id_provided(self): self.fake_config['profiles']['development']['external_id'] = 'myid' response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', 'Expiration': datetime.now(tzlocal()).isoformat(), }, } client_creator = self.create_client_creator(with_response=response) provider = credentials.AssumeRoleProvider( self.create_config_loader(), client_creator, cache={}, profile_name='development') provider.load() client = client_creator.return_value client.assume_role.assert_called_with( RoleArn='myrole', ExternalId='myid', RoleSessionName=mock.ANY)
def test_assume_role_retrieves_from_cache(self): date_in_future = datetime.utcnow() + timedelta(seconds=1000) utc_timestamp = date_in_future.isoformat() + 'Z' self.fake_config['profiles']['development']['role_arn'] = 'myrole' cache = { 'development--myrole': { 'Credentials': { 'AccessKeyId': 'foo-cached', 'SecretAccessKey': 'bar-cached', 'SessionToken': 'baz-cached', 'Expiration': utc_timestamp, } } } provider = credentials.AssumeRoleProvider( self.create_config_loader(), mock.Mock(), cache=cache, profile_name='development') creds = provider.load() self.assertEqual(creds.access_key, 'foo-cached') self.assertEqual(creds.secret_key, 'bar-cached') self.assertEqual(creds.token, 'baz-cached')
def test_cache_key_is_windows_safe(self): response = { 'Credentials': { 'AccessKeyId': 'foo', 'SecretAccessKey': 'bar', 'SessionToken': 'baz', 'Expiration': datetime.now(tzlocal()).isoformat() }, } cache = {} self.fake_config['profiles']['development']['role_arn'] = ( 'arn:aws:iam::foo-role') client_creator = self.create_client_creator(with_response=response) provider = credentials.AssumeRoleProvider(self.create_config_loader(), client_creator, cache=cache, profile_name='development') provider.load() # On windows, you cannot use a a ':' in the filename, so # we need to do some small transformations on the filename # to replace any ':' that come up. self.assertEqual(cache['development--arn_aws_iam__foo-role'], response)