def fetch_session_token(mfa_token: str) -> Result: result = Result() credentials_file = _load_credentials_file() logger.info('fetch session-token') profile = 'session-token' try: secrets = _get_session_token(mfa_token) except ClientError: error_text = 'could not fetch session token' result.error(error_text) logger.error(error_text, exc_info=True) return result except ParamValidationError: error_text = 'invalid mfa token' result.error(error_text) logger.error(error_text, exc_info=True) return result except NoCredentialsError: error_text = 'access_key credentials invalid' result.error(error_text) logger.error(error_text, exc_info=True) return result _add_profile_credentials(credentials_file, profile, secrets) _write_credentials_file(credentials_file) logger.info('session-token successfully fetched') result.set_success() return result
def test_error(self): result = Result() error_message = 'there was an error' result.error(error_message) self.assertEqual(False, result.was_success) self.assertEqual(True, result.was_error) self.assertEqual(error_message, result.error_message)
def login(self, profile_group: ProfileGroup, mfa_callback: Callable) -> Result: result = Result() logger.info(f'start login {profile_group.name}') self.active_profile_group = profile_group access_key_result = credentials.check_access_key() if not access_key_result.was_success: return access_key_result session_result = credentials.check_session() if session_result.was_error: return session_result if not session_result.was_success: renew_session_result = self._renew_session(mfa_callback) if not renew_session_result.was_success: return renew_session_result user_name = credentials.get_user_name() role_result = credentials.fetch_role_credentials( user_name, profile_group) if not role_result.was_success: return role_result set_region_result = self.set_region(self.region_override) if not set_region_result.was_success: return set_region_result logger.info('login success') self._handle_support_files(profile_group) result.set_success() return result
def rotate_access_key() -> Result: result = Result() logger.info('initiate key rotation') logger.info('check access key') access_key_result = credentials.check_access_key() if not access_key_result.was_success: return access_key_result logger.info('check session') check_session_result = credentials.check_session() if not check_session_result.was_success: check_session_result.error('Access Denied. Please log first') return check_session_result logger.info('create key') user = credentials.get_user_name() create_access_key_result = iam.create_access_key(user) if not create_access_key_result.was_success: return create_access_key_result logger.info('delete key') previous_access_key_id = credentials.get_access_key_id() iam.delete_iam_access_key(user, previous_access_key_id) logger.info('save key') credentials.set_access_key( key_id=create_access_key_result.payload['AccessKeyId'], access_key=create_access_key_result.payload['SecretAccessKey']) result.set_success() return result
def set_region(self, region: str) -> Result: self.region_override = region if not self.active_profile_group: result = Result() result.set_success() return result return credentials.write_profile_config(self.active_profile_group, self.get_region())
def _renew_session(self, mfa_callback: Callable) -> Result: logger.info('renew session') logger.info('get mfa from console') token = mfa.fetch_mfa_token_from_shell(self.config.mfa_shell_command) if not token: logger.info('get mfa from user') token = mfa_callback() if not token: result = Result() result.error('invalid mfa token') return result session_result = credentials.fetch_session_token(token) return session_result
def logout(self): result = Result() logger.info(f'start logout') role_result = credentials.fetch_role_credentials( user_name='none', profile_group=self.empty_profile_group) if not role_result.was_success: return role_result config_result = credentials.write_profile_config( profile_group=self.empty_profile_group, region='') if not config_result.was_success: return config_result logger.info('logout success') result.set_success() return result
def fetch_role_credentials(user_name: str, profile_group: ProfileGroup) -> Result: result = Result() credentials_file = _load_credentials_file() logger.info('fetch role credentials') try: for profile in profile_group.profiles: logger.info(f'fetch {profile.profile}') source_profile = profile.source or 'session-token' secrets = _assume_role(source_profile, user_name, profile.account, profile.role) _add_profile_credentials(credentials_file, profile.profile, secrets) if profile.default: _add_profile_credentials(credentials_file, 'default', secrets) _write_credentials_file(credentials_file) credentials_file = _remove_unused_profiles(credentials_file, profile_group) except Exception: error_text = 'error while fetching role credentials' result.error(error_text) logger.error(error_text, exc_info=True) return result _write_credentials_file(credentials_file) result.set_success() return result
def test_rotate_access_key__successful_rotate(self, mock_credentials, mock_iam): mock_credentials.check_access_key.return_value = self.success_result mock_credentials.check_session.return_value = self.success_result mock_credentials.get_user_name.return_value = 'test-user' mock_credentials.get_access_key_id.return_value = '12345' access_key_result = Result() access_key_result.add_payload({ 'AccessKeyId': 12345, 'SecretAccessKey': 67890 }) access_key_result.set_success() mock_iam.create_access_key.return_value = access_key_result result = self.core.rotate_access_key() expected = [call.check_access_key(), call.check_session(), call.get_user_name(), call.get_access_key_id(), call.set_access_key(key_id=12345, access_key=67890)] self.assertEqual(expected, mock_credentials.mock_calls) expected = [call.create_access_key('test-user'), call.delete_iam_access_key('test-user', '12345')] self.assertEqual(expected, mock_iam.mock_calls) self.assertEqual(True, result.was_success) self.assertEqual(False, result.was_error)
def create_access_key(user_name) -> Result: result = Result() session = boto3.Session(profile_name='session-token') client = session.client('iam') try: response = client.create_access_key(UserName=user_name) except client.exceptions.LimitExceededException: error_text = 'key limit reached, creation failed' logger.warning(error_text) result.error(error_text) return result if 'AccessKey' not in response: error_text = 'unknown error with iam' logger.error(error_text) logger.error(response) result.error(error_text) return result result.add_payload(response['AccessKey']) result.set_success() return result
def edit_config(self, config: Config) -> Result: result = Result() try: self.config = config self.config.save_to_disk() except Exception as error: logger.error(str(error), exc_info=True) result.error('could not save config') return result result.set_success() return result
def has_access_key() -> Result: logger.info('has access key') result = Result() credentials_file = _load_credentials_file() if not credentials_file.has_section('access-key'): error_text = 'could not find profile \'access-key\' in .aws/credentials' result.error(error_text) logger.warning(error_text) return result result.set_success() return result
def check_access_key() -> Result: logger.info('check access key') access_key_result = has_access_key() if not access_key_result.was_success: return access_key_result result = Result() try: client = _get_client('access-key', 'sts', timeout=2, retries=2) client.get_caller_identity() except ClientError: error_text = 'access key is not valid' result.error(error_text) logger.warning(error_text) return result except (EndpointConnectionError, ReadTimeoutError): error_text = 'could not reach sts service' result.error(error_text) logger.error(error_text, exc_info=True) return result result.set_success() return result
def delete_iam_access_key(user_name, key_id): result = Result() session = boto3.Session(profile_name='session-token') client = session.client('iam') try: client.delete_access_key(UserName=user_name, AccessKeyId=key_id) except Exception: error_text = 'could not remove old key' logger.error(error_text, exc_info=True) result.error(error_text) return result result.set_success() return result
def write_profile_config(profile_group: ProfileGroup, region: str): result = Result() config_file = _load_config_file() try: for profile in profile_group.profiles: logger.info(f'add config for {profile.profile}') _add_profile_config(config_file, profile.profile, region) if profile.default: _add_profile_config(config_file, 'default', region) config_file = _remove_unused_configs(config_file, profile_group) except Exception: error_text = 'error writing config' result.error(error_text) logger.error(error_text, exc_info=True) return result _write_config_file(config_file) result.set_success() return result
def check_session() -> Result: result = Result() credentials_file = _load_credentials_file() if not credentials_file.has_section('session-token'): logger.warning('no session token found') return result try: client = _get_client('session-token', 'sts', timeout=2, retries=2) client.get_caller_identity() except ClientError: # this is the normal case when the session token is not valid. Proceed then to fetch a new one return result except (EndpointConnectionError, ReadTimeoutError): error_text = 'could not reach sts service' result.error(error_text) logger.error(error_text, exc_info=True) return result result.set_success() return result
def get_failed_result() -> Result: return Result()
def get_error_result() -> Result: result = Result() result.error('some error') return result
def get_success_result() -> Result: result = Result() result.set_success() return result
def test___unsuccessful_by_default(self): result = Result() self.assertEqual(False, result.was_success) self.assertEqual(False, result.was_error)
def test_set_success(self): result = Result() result.set_success() self.assertEqual(True, result.was_success) self.assertEqual(False, result.was_error)
def test_add_payload(self): result = Result() result.add_payload('test') self.assertEqual(False, result.was_success) self.assertEqual(False, result.was_error) self.assertEqual('test', result.payload)