From 99d71981cf4885a3f5873752a41da46b6a2d6571 Mon Sep 17 00:00:00 2001 From: Eric Pierce Date: Sun, 7 Apr 2024 07:52:00 -0700 Subject: [PATCH 1/6] Fix force_classic parameter from config file --- gimme_aws_creds/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gimme_aws_creds/main.py b/gimme_aws_creds/main.py index c33627c..5a2168f 100644 --- a/gimme_aws_creds/main.py +++ b/gimme_aws_creds/main.py @@ -506,7 +506,7 @@ def okta_platform(self): return self._cache['okta_platform'] # Treat this domain as classic, even if it's OIE - if self.config.force_classic == True or self.conf_dict.get('force_classic') == "True": + if self.config.force_classic is True or self.conf_dict.get('force_classic') is True: self.ui.message('Okta Classic login flow enabled') self.set_okta_platform('classic') return 'classic' From 2474038199c0a77d7172b0b55fed48ae8c091f74 Mon Sep 17 00:00:00 2001 From: Eric Pierce Date: Sun, 7 Apr 2024 08:04:41 -0700 Subject: [PATCH 2/6] Handle errors when provided MFA doesn't meet policy requirements in OIE --- gimme_aws_creds/okta_classic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gimme_aws_creds/okta_classic.py b/gimme_aws_creds/okta_classic.py index 21cfc41..32cfd31 100644 --- a/gimme_aws_creds/okta_classic.py +++ b/gimme_aws_creds/okta_classic.py @@ -257,6 +257,11 @@ def auth_oauth(self, client_id, **kwargs): ) response.raise_for_status() + # If we didn't get a 302 redirect, the MFA factor didn't meet the requirement for the app + if 'Location' not in response.headers: + raise errors.GimmeAWSCredsError( + "LOGIN ERROR: Provided MFA factor does not meet the authentication policies for this application", 2 + ) url_parse_results = urlparse(response.headers['Location']) query_result = parse_qs(url_parse_results.fragment) From d81340d4252a53d5dcaf503c46092c592c3e07aa Mon Sep 17 00:00:00 2001 From: Eric Pierce Date: Sun, 7 Apr 2024 08:12:22 -0700 Subject: [PATCH 3/6] Disable the DT cookie when using 'force_classic' mode --- gimme_aws_creds/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gimme_aws_creds/main.py b/gimme_aws_creds/main.py index 5a2168f..9bd9986 100644 --- a/gimme_aws_creds/main.py +++ b/gimme_aws_creds/main.py @@ -604,7 +604,7 @@ def get_resolver(self): @property def device_token(self): - if self.config.action_register_device is True: + if self.config.action_register_device is True or self.conf_dict.get('force_classic') is True: self.conf_dict['device_token'] = None return self.conf_dict.get('device_token') @@ -941,7 +941,7 @@ def handle_action_store_json_creds(self, stream=None): def handle_action_register_device(self): # Capture the Device Token and write it to the config file - if self.okta_platform == "classic" and ( not self.device_token or self.config.action_register_device is True ): + if self.okta_platform == "classic" and (self.conf_dict.get('force_classic') is not True) and ( not self.device_token or self.config.action_register_device is True ): if not self.config.action_register_device: self.ui.notify('\n*** No device token found in configuration file, it will be created.') self.ui.notify('*** You may be prompted for MFA more than once for this run.\n') From 4174cb560fb9265986e241751b38d674df550de1 Mon Sep 17 00:00:00 2001 From: Eric Pierce Date: Sun, 7 Apr 2024 08:42:24 -0700 Subject: [PATCH 4/6] Change package file name to match values expected by homebrew Homebrew was missing package updated because of the file name change from gimme_aws_creds to gimme-aws-creds --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 10961e7..a7b6f98 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ requirements = f.read().splitlines() setup( - name='gimme-aws-creds', + name='gimme_aws_creds', version='2.8.0', install_requires=requirements, author='Eric Pierce', From d80017ec6e3269da4d8023c720ca36871e5a65be Mon Sep 17 00:00:00 2001 From: Eric Pierce Date: Fri, 19 Apr 2024 07:33:50 -0700 Subject: [PATCH 5/6] Change default behavior for a missing value for force_classic in the config file Migrating from Okta Classic to OIE requires all users to set force_classic=True or start using --force_classic. Changing the default behavior for a missing value for force_classic will allow users to continue using gimme-aws-creds without having to touch their configuration file --- gimme_aws_creds/config.py | 4 ++++ gimme_aws_creds/main.py | 15 +++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/gimme_aws_creds/config.py b/gimme_aws_creds/config.py index 01dc6ea..fe541d3 100644 --- a/gimme_aws_creds/config.py +++ b/gimme_aws_creds/config.py @@ -201,6 +201,10 @@ def _handle_config(self, config, profile_config, include_inherits = True): profile_config[key] = True elif profile_config[key] == 'False': profile_config[key] = False + + # Empty string in force_classic should be handled as True - this makes sure that migrating from Classic to OIE is seamless + if profile_config.get('force_classic') == '' or profile_config.get('force_classic') is None: + profile_config['force_classic'] = True if "inherits" in profile_config.keys() and include_inherits: self.ui.message("Using inherited config: " + profile_config["inherits"]) diff --git a/gimme_aws_creds/main.py b/gimme_aws_creds/main.py index 9e944e9..44349ae 100644 --- a/gimme_aws_creds/main.py +++ b/gimme_aws_creds/main.py @@ -505,12 +505,6 @@ def okta_platform(self): if 'okta_platform' in self._cache: return self._cache['okta_platform'] - # Treat this domain as classic, even if it's OIE - if self.config.force_classic is True or self.conf_dict.get('force_classic') is True: - self.ui.message('Okta Classic login flow enabled') - self.set_okta_platform('classic') - return 'classic' - response = requests.get( self.okta_org_url + '/.well-known/okta-organization', headers={ @@ -527,8 +521,13 @@ def okta_platform(self): ret = 'classic' elif response_data['pipeline'] == 'idx': ret = 'identity_engine' - if not self.conf_dict.get('client_id'): - raise errors.GimmeAWSCredsError('OAuth Client ID is required for Okta Identity Engine domains. Try running --config again.') + # Force_Classic is set - treat this domain as classic + if self.config.force_classic is True or self.conf_dict.get('force_classic') is True: + self.ui.message('Okta Classic login flow enabled') + ret = 'classic' + else: + if not self.conf_dict.get('client_id'): + raise errors.GimmeAWSCredsError('OAuth Client ID is required for Okta Identity Engine domains. Try running --config again.') else: raise RuntimeError('Unknown Okta platform type: {}'.format(response_data['pipeline'])) else: From 5e640c4bb84c25372605db246b7b5df93cf64f70 Mon Sep 17 00:00:00 2001 From: Eric Pierce Date: Fri, 19 Apr 2024 07:40:25 -0700 Subject: [PATCH 6/6] Fix tests --- tests/test_config.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index a100201..fc1390f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -61,7 +61,7 @@ def test_read_config(self): config = Config(gac_ui=test_ui, create_config=False) config.conf_profile = "myprofile" profile_config = config.get_config_dict() - self.assertEqual(profile_config, {"client_id": "foo"}) + self.assertEqual(profile_config, {"client_id": "foo", 'force_classic': True}) def test_read_config_inherited(self): """Test to make sure getting config works when inherited""" @@ -75,6 +75,7 @@ def test_read_config_inherited(self): [mybase] client_id = bar aws_appname = baz + force_classic = True [myprofile] inherits = mybase client_id = foo @@ -89,6 +90,7 @@ def test_read_config_inherited(self): "client_id": "foo", "aws_appname": "baz", "aws_rolename": "myrole", + 'force_classic': True, }) def test_read_nested_config_inherited(self): @@ -104,6 +106,7 @@ def test_read_nested_config_inherited(self): [mybase-level2] inherits = mybase-level1 aws_appname = baz +force_classic = [myprofile] inherits = mybase-level2 client_id = foo @@ -116,6 +119,7 @@ def test_read_nested_config_inherited(self): "client_id": "foo", "aws_appname": "baz", "aws_rolename": "myrole", + "force_classic": True }) def test_fail_if_profile_not_found(self):