Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support arbitrary delimiters in acc-role profile names #475

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ A configuration wizard will prompt you to enter the necessary configuration para
- cred_profile - If writing to the AWS cred file, this sets the name of the AWS credential profile.
- The reserved word `role` will use the name component of the role arn as the profile name. i.e. arn:aws:iam::123456789012:role/okta-1234-role becomes section [okta-1234-role] in the aws credentials file
- The reserved word `acc` will use the account number (or alias if `resolve_aws_alias` is set to y) as the profile name. i.e. arn:aws:iam::123456789012:role/okta-1234-role becomes section [arn:aws:iam::123456789012] or if `resolve_aws_alias` [okta-1234-role] in the aws credentials file.
- The reserved word `acc-role` will use the name component of the role arn prepended with account number (or alias if `resolve_aws_alias` is set to y) to avoid collisions, i.e. arn:aws:iam::123456789012:role/okta-1234-role becomes section [123456789012-okta-1234-role], or if `resolve_aws_alias` [okta-1234-role] in the aws credentials file
- The reserved word `acc-role` will use the name component of the role arn prepended with account number (or alias if `resolve_aws_alias` is set to y) to avoid collisions, i.e. arn:aws:iam::123456789012:role/okta-1234-role becomes section [123456789012-okta-1234-role], or if `resolve_aws_alias` [okta-1234-role] in the aws credentials file. The hyphen may be replaced by any character.
- If set to `default` then the temp creds will be stored in the default profile
- Note: if there are multiple roles, and `default` is selected it will be overwritten multiple times and last role wins. The same happens when `role` is selected and you have many accounts with the same role names. Consider using `acc-role` if this happens.
- aws_appname - This is optional. The Okta AWS App name, which has the role you want to assume.
Expand Down
7 changes: 3 additions & 4 deletions gimme_aws_creds/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import requests
from urllib.parse import urlparse

from . import errors, ui, version
from . import errors, profiles, ui, version


class Config(object):
Expand Down Expand Up @@ -513,16 +513,15 @@ def _get_cred_profile(self, default_entry):
"The AWS credential profile defines which profile is used to store the temp AWS creds.\n"
"If set to 'role' then a new profile will be created matching the role name assumed by the user.\n"
"If set to 'acc' then a new profile will be created matching the account number.\n"
"If set to 'acc-role' then a new profile will be created matching the role name assumed by the user, but prefixed with account number to avoid collisions.\n"
"If set to 'acc-role' then a new profile will be created matching the role name assumed by the user, but prefixed with account number (or alias if resolve_alias is true) to avoid collisions. Any character may be substituted for the hyphen.\n"
"If set to 'default' then the temp creds will be stored in the default profile\n"
"If set to any other value, the name of the profile will match that value."
)

cred_profile = self._get_user_input(
"AWS Credential Profile", default_entry)

if cred_profile.lower() in ['default', 'role', 'acc', 'acc-role']:
cred_profile = cred_profile.lower()
cred_profile = profiles.Profile(cred_profile).canonicalize()

return cred_profile

Expand Down
21 changes: 4 additions & 17 deletions gimme_aws_creds/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from .okta_identity_engine import OktaIdentityEngine
from .okta_classic import OktaClassicClient
from .registered_authenticators import RegisteredAuthenticators
from .profiles import Profile


class GimmeAWSCreds(object):
Expand Down Expand Up @@ -809,23 +810,9 @@ def prepare_data(self, role, generate_credentials=False):
}

def get_profile_name(self, cred_profile, include_path, naming_data, resolve_alias, role):
if cred_profile.lower() == 'default':
profile_name = 'default'
elif cred_profile.lower() == 'role':
profile_name = naming_data['role']
elif cred_profile.lower() == 'acc':
profile_name = self._get_account_name(naming_data['account'], role, resolve_alias)
elif cred_profile.lower() == 'acc-role':
account = self._get_account_name(naming_data['account'], role, resolve_alias)
role_name = naming_data['role']
path = naming_data['path']
if include_path is True:
role_name = ''.join([path, role_name])
profile_name = '-'.join([account,
role_name])
else:
profile_name = cred_profile
return profile_name
cred_profile = Profile(cred_profile, include_path)
account = self._get_account_name(naming_data['account'], role, resolve_alias)
return cred_profile.name_for(account, naming_data['role'], naming_data['path'])

def _get_account_name(self, account, role, resolve_alias):
if resolve_alias is False:
Expand Down
34 changes: 34 additions & 0 deletions gimme_aws_creds/profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
The Profile class generates a profile name from the cred_profile configuration parameter.
"""
class Profile:
def __init__(self, cred_profile, include_path):
self.cred_profile = cred_profile
self.include_path = include_path

def canonicalize(self):
lc_profile = self.cred_profile.lower()
if lc_profile in ['default', 'role', 'acc'] or self._is_delimited(lc_profile):
return lc_profile
else:
return self.cred_profile

def _is_delimited(self, lc_profile):
if len(lc_profile)==8 and lc_profile.startswith('acc') and lc_profile.endswith('role'):
return lc_profile[3]
return False

def name_for(self, account, role_name, path='/'):
lc_profile = self.cred_profile.lower()
if lc_profile == 'default':
return 'default'
elif lc_profile == 'role':
return role_name
elif lc_profile == 'acc':
return account
elif delimiter := self._is_delimited(lc_profile):
if self.include_path:
role_name = ''.join([path, role_name])
return delimiter.join([account, role_name])
else:
return self.cred_profile
27 changes: 27 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,19 @@ def test_get_profile_name_accrole_resolve_alias_do_not_include_paths(self):
include_path = False
self.assertEqual(creds.get_profile_name(cred_profile, include_path, naming_data, resolve_alias, role), "my-org-master-administrator")

def test_get_profile_name_accrole_colon_resolve_alias_do_not_include_paths(self):
"Testing the acc:role, with alias resolution, and not including full role path"
creds = GimmeAWSCreds()
naming_data = {'account': '123456789012', 'role': 'administrator', 'path': '/administrator/'}
role = RoleSet(idp='arn:aws:iam::123456789012:saml-provider/my-okta-provider',
role='arn:aws:iam::123456789012:role/administrator/administrator',
friendly_account_name='Account: my-org-master (123456789012)',
friendly_role_name='administrator/administrator')
cred_profile = 'acc:role'
resolve_alias = True
include_path = False
self.assertEqual(creds.get_profile_name(cred_profile, include_path, naming_data, resolve_alias, role), "my-org-master:administrator")

def test_get_profile_accrole_name_do_not_resolve_alias_do_not_include_paths(self):
"Testing the acc-role, without alias resolution, and not including full role path"
creds = GimmeAWSCreds()
Expand All @@ -215,6 +228,20 @@ def test_get_profile_accrole_name_do_not_resolve_alias_do_not_include_paths(self
self.assertEqual(creds.get_profile_name(cred_profile, include_path, naming_data, resolve_alias, role),
"123456789012-administrator")

def test_get_profile_acc_slash_role_name_do_not_resolve_alias_do_not_include_paths(self):
"Testing the acc/role, without alias resolution, and not including full role path"
creds = GimmeAWSCreds()
naming_data = {'account': '123456789012', 'role': 'administrator', 'path': '/administrator/'}
role = RoleSet(idp='arn:aws:iam::123456789012:saml-provider/my-okta-provider',
role='arn:aws:iam::123456789012:role/administrator/administrator',
friendly_account_name='Account: my-org-master (123456789012)',
friendly_role_name='administrator/administrator')
cred_profile = 'acc/role'
resolve_alias = False
include_path = False
self.assertEqual(creds.get_profile_name(cred_profile, include_path, naming_data, resolve_alias, role),
"123456789012/administrator")

def test_get_profile_accrole_name_do_not_resolve_alias_include_paths(self):
"Testing the acc-role, without alias resolution, and including full role path"
creds = GimmeAWSCreds()
Expand Down