diff --git a/README.md b/README.md index 75b3442d..37ef01a0 100644 --- a/README.md +++ b/README.md @@ -287,8 +287,10 @@ Then, you can choose the newly registered authenticator from the factors list. ## Running Tests You can run all the unit tests using nosetests. Most of the tests are mocked. +Make sure to install the dev requirements before running nosetests. ```bash +pip3 install -r requirements_dev.txt nosetests --verbosity=2 tests/ ``` diff --git a/gimme_aws_creds/config.py b/gimme_aws_creds/config.py index cbd99d53..9278f534 100644 --- a/gimme_aws_creds/config.py +++ b/gimme_aws_creds/config.py @@ -173,7 +173,7 @@ def get_args(self): self.output_format = args.output_format if args.roles is not None: self.roles = [role.strip() for role in args.roles.split(',') if role.strip()] - self.conf_profile = args.profile or 'DEFAULT' + self.conf_profile = args.profile def _handle_config(self, config, profile_config, include_inherits = True): if "inherits" in profile_config.keys() and include_inherits: @@ -197,16 +197,57 @@ def get_config_dict(self, include_inherits = True): config = configparser.ConfigParser() config.read(self.OKTA_CONFIG) - try: - profile_config = dict(config[self.conf_profile]) - self.fail_if_profile_not_found(profile_config, self.conf_profile, config.default_section) - return self._handle_config(config, profile_config, include_inherits) - except KeyError: - if self.action_configure: - return {} - raise errors.GimmeAWSCredsError( - 'Configuration profile not found! Use the --action-configure flag to generate the profile.') - raise errors.GimmeAWSCredsError('Configuration file not found! Use the --action-configure flag to generate file.') + selected_section = None + if self.conf_profile is not None: + try: + selected_section = config[self.conf_profile] + if self.conf_profile == 'DEFAULT': + found_default_option = False + for _ in selected_section: + found_default_option = True + break + if not found_default_option: + selected_section = None + + except KeyError: + raise errors.GimmeAWSCredsError( + 'Configuration profile not found! Did you spell the profile name incorrectly? If not, ' + 'use the --action-configure flag to generate a profile named {}.'.format(self.conf_profile)) + else: + self.ui.message('The --profile switch has not been set. Searching for an automatic profile...') + + if selected_section is None: + if self.ui.environ.get('AWS_PROFILE') is not None: + aws_env_profile = self.ui.environ.get('AWS_PROFILE') + self.ui.message('The AWS_PROFILE environment variable is set. Searching for a profile ' + 'where `aws_profile_env_match = {}`'.format(aws_env_profile)) + for section_name in config.sections(): + section = config[section_name] + # Use a different key than `cred_profile` for e.g., cases where gimme-aws-creds + # manages base credentials but elevated credentials are acquired via the STS AssumeRole + # action using a different AWS profile name + if 'aws_profile_env_match' in section and section['aws_profile_env_match'] == aws_env_profile: + self.ui.message(' Match found! Using the [{}] profile'.format(section.name)) + selected_section = section + break + else: + self.ui.message("The AWS_PROFILE environment variable is not set. Can't search for existing " + "profiles with a matching `aws_profile_env_match` attribute.") + + if selected_section is not None: + return self._handle_config(config, dict(selected_section), include_inherits) + + if self.action_configure: + return {} + + # configparser returns an empty dictionary instead of raising KeyError for missing default section + defaults_dict = config.defaults() + if defaults_dict: + return self._handle_config(config, defaults_dict, include_inherits) + + raise errors.GimmeAWSCredsError( + 'No profile found and DEFAULT profile is missing. Please specify a profile or run again with ' + '`--action-configure` to generate a profile') def update_config_file(self): """ @@ -568,14 +609,3 @@ def clean_up(self): """ clean up secret stuff""" del self.username del self.api_key - - def fail_if_profile_not_found(self, profile_config, conf_profile, default_section): - """ - When a users profile does not have a profile named 'DEFAULT' configparser fails to throw - an exception. This will raise an exception that handles this case and provide better messaging - to the user why the failure occurred. - Ensure that whichever profile is set as the default exists in the end users okta config - """ - if not profile_config and conf_profile == default_section: - raise errors.GimmeAWSCredsError( - 'DEFAULT profile is missing! This is profile is required when not using --profile') \ No newline at end of file diff --git a/tests/test_config.py b/tests/test_config.py index a6daffaf..8daab6fa 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -127,5 +127,5 @@ def test_fail_if_profile_not_found(self): config.conf_profile = "DEFAULT" with self.assertRaises(errors.GimmeAWSCredsError) as context: config.get_config_dict() - self.assertTrue('DEFAULT profile is missing! This is profile is required when not using --profile' == context.exception.message) + self.assertTrue('No profile found and DEFAULT profile is missing. Please specify a profile or run again with `--action-configure` to generate a profile' == context.exception.message)