-
Notifications
You must be signed in to change notification settings - Fork 27
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: Add support for optional profile expiration #153
Open
ruhulio
wants to merge
2
commits into
dowjones:main
Choose a base branch
from
ruhulio:feature/profile-expiration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,7 +5,7 @@ | |||||
import builtins | ||||||
import codecs | ||||||
import configparser | ||||||
from datetime import timezone | ||||||
from datetime import datetime, timezone | ||||||
from getpass import getpass | ||||||
import json | ||||||
import logging | ||||||
|
@@ -64,6 +64,8 @@ def cmd_interface(args): | |||||
) | ||||||
sys.exit(1) | ||||||
|
||||||
check_profile_expiration(config) | ||||||
|
||||||
if config.user["use_device_token"]: | ||||||
device_token = config.okta["device_token"] | ||||||
if device_token: | ||||||
|
@@ -109,11 +111,7 @@ def cmd_interface(args): | |||||
output=config.aws["output"], | ||||||
) | ||||||
|
||||||
device_token = HTTP_client.get_device_token() | ||||||
if config.user["use_device_token"] and device_token: | ||||||
logger.info(f"Saving device token to config profile {args.user_config_profile}") | ||||||
config.okta["device_token"] = device_token | ||||||
update_device_token(config) | ||||||
update_profile(config, role_response) | ||||||
|
||||||
display_selected_role(profile_name=config.aws["profile"], role_response=role_response) | ||||||
|
||||||
|
@@ -235,6 +233,13 @@ def parse_cli_args(args): | |||||
default=False, | ||||||
help="Use device token across sessions", | ||||||
) | ||||||
parser.add_argument( | ||||||
"--use-profile-expiration", | ||||||
dest="user_use_profile_expiration", | ||||||
action="store_true", | ||||||
default=False, | ||||||
help="Use profile expiration to bypass re-authenticating", | ||||||
) | ||||||
parser.add_argument( | ||||||
"--quiet", | ||||||
dest="user_quiet", | ||||||
|
@@ -517,6 +522,65 @@ def prompt_role_choices(aut_tiles): | |||||
return selected_role | ||||||
|
||||||
|
||||||
def get_role_expiration(role_response={}): | ||||||
"""Get the expiration from the role response. | ||||||
|
||||||
:param role_response: Assume Role response dict | ||||||
:return expiration | ||||||
""" | ||||||
try: | ||||||
return role_response["Credentials"]["Expiration"] | ||||||
except (KeyError, TypeError) as err: | ||||||
logger.error(f"Could not retrieve expiration: {err}") | ||||||
sys.exit(1) | ||||||
|
||||||
|
||||||
def check_profile_expiration(config): | ||||||
"""Check profile expiration and exit if still valid. | ||||||
|
||||||
:param config: Config object | ||||||
""" | ||||||
if not config.user["use_profile_expiration"]: | ||||||
return | ||||||
|
||||||
profile = config.aws["profile"] | ||||||
|
||||||
profile_expiration_str = config.okta["profile_expiration"] | ||||||
if not profile_expiration_str: | ||||||
logger.warning(f"Expiration unavailable for config profile {profile}. ") | ||||||
return | ||||||
|
||||||
profile_expiration = datetime.fromisoformat(profile_expiration_str) | ||||||
now = datetime.now(timezone.utc) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably not necessary but if it isn't guaranteed to return utc.
Suggested change
|
||||||
|
||||||
if now < profile_expiration: | ||||||
logger.info(f"Expiration for config profile {profile} is still valid: {profile_expiration}") | ||||||
sys.exit(0) | ||||||
else: | ||||||
logger.warning( | ||||||
f"Expiration for config profile {profile} is no longer valid: {profile_expiration}" | ||||||
) | ||||||
|
||||||
|
||||||
def update_profile(config, role_response={}): | ||||||
"""Update profile with device token and expiration if needed. | ||||||
|
||||||
:param config: Config object | ||||||
:param role_response: Assume Role response dict | ||||||
""" | ||||||
device_token = HTTP_client.get_device_token() | ||||||
if config.user["use_device_token"] and device_token: | ||||||
logger.info(f"Saving device token to config profile {config.aws['profile']}") | ||||||
config.okta["device_token"] = device_token | ||||||
update_profile_device_token(config) | ||||||
|
||||||
role_expiration = get_role_expiration(role_response) | ||||||
if config.user["use_profile_expiration"] and role_expiration: | ||||||
logger.info(f"Saving expiration to config profile {config.aws['profile']}") | ||||||
config.okta["profile_expiration"] = role_expiration | ||||||
update_profile_expiration(config) | ||||||
|
||||||
|
||||||
def display_selected_role(profile_name="", role_response={}): | ||||||
"""Print details about how to assume role. | ||||||
|
||||||
|
@@ -525,21 +589,16 @@ def display_selected_role(profile_name="", role_response={}): | |||||
:return: message displayed. | ||||||
|
||||||
""" | ||||||
try: | ||||||
expiration_time = role_response["Credentials"]["Expiration"] | ||||||
except (KeyError, TypeError) as err: | ||||||
logger.error(f"Could not retrieve expiration time: {err}") | ||||||
sys.exit(1) | ||||||
|
||||||
expiration_time_local = utc_to_local(expiration_time) | ||||||
role_expiration = get_role_expiration(role_response) | ||||||
role_expiration_local = utc_to_local(role_expiration) | ||||||
msg = ( | ||||||
f"\nGenerated profile '{profile_name}' in " | ||||||
f"{config.aws['shared_credentials_file']}.\n" | ||||||
"\nUse profile to authenticate to AWS:\n\t" | ||||||
f"aws --profile '{profile_name}' sts get-caller-identity" | ||||||
"\nOR\n\t" | ||||||
f"export AWS_PROFILE='{profile_name}'\n\n" | ||||||
f"Credentials are valid until {expiration_time} ({expiration_time_local})." | ||||||
f"Credentials are valid until {role_expiration} ({role_expiration_local})." | ||||||
) | ||||||
|
||||||
print(msg) | ||||||
|
@@ -775,7 +834,7 @@ def process_environment(prefix="tokendito"): | |||||
|
||||||
def process_interactive_input(config, skip_password=False): | ||||||
""" | ||||||
Request input interactively interactively for elements that are not proesent. | ||||||
Request input interactively interactively for elements that are not present. | ||||||
|
||||||
:param config: Config object with some values set. | ||||||
:param skip_password: Whether or not ask the user for a password. | ||||||
|
@@ -1002,7 +1061,7 @@ def update_configuration(config): | |||||
logger.info(f"Updated {ini_file} with profile {profile}") | ||||||
|
||||||
|
||||||
def update_device_token(config): | ||||||
def update_profile_device_token(config): | ||||||
"""Update configuration file on local system with device token. | ||||||
|
||||||
:param config: the current configuration | ||||||
|
@@ -1023,6 +1082,27 @@ def update_device_token(config): | |||||
logger.info(f"Updated {ini_file} with profile {profile}") | ||||||
|
||||||
|
||||||
def update_profile_expiration(config): | ||||||
"""Update configuration file on local system with profile expiration. | ||||||
|
||||||
:param config: the current configuration | ||||||
:return: None | ||||||
""" | ||||||
logger.debug("Update configuration file on local system with profile expiration.") | ||||||
ini_file = config.user["config_file"] | ||||||
profile = config.user["config_profile"] | ||||||
|
||||||
contents = {} | ||||||
# Copy relevant parts of the configuration into an dictionary that | ||||||
# will be written out to disk | ||||||
if "profile_expiration" in config.okta and config.okta["profile_expiration"] is not None: | ||||||
contents["okta_profile_expiration"] = config.okta["profile_expiration"] | ||||||
|
||||||
logger.debug(f"Adding {contents} to config file.") | ||||||
update_ini(profile=profile, ini_file=ini_file, **contents) | ||||||
logger.info(f"Updated {ini_file} with profile {profile}") | ||||||
|
||||||
|
||||||
def set_local_credentials(response={}, role="default", region="us-east-1", output="json"): | ||||||
"""Write to local files to insert credentials. | ||||||
|
||||||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming here that aws returns the expiration time in utc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, the value is returned in UTC.
There is existing code that converts this to local when displaying the role to the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup yup! I just knew there was a aws config var for setting timestamps. I have had a problem switching from aws cli version 1 to 2 before. A different issue than this, but just wanted to check as those returned different formats. Think we are good then 👍 !