From bb4a521d7eba05a3cc6a802416124029af9222f9 Mon Sep 17 00:00:00 2001 From: jessebot Date: Sun, 18 May 2025 17:44:05 +0200 Subject: [PATCH 1/6] lock dependencies, support python 3.10+, and change assume_role variable to role everywhere to avoid confusion and promote brevity --- awth/__init__.py | 50 +++++++------- poetry.lock | 173 +++++++++++++++++++++++++++++++++-------------- pyproject.toml | 4 +- 3 files changed, 149 insertions(+), 78 deletions(-) diff --git a/awth/__init__.py b/awth/__init__.py index 4f4f1e1..d1969f1 100644 --- a/awth/__init__.py +++ b/awth/__init__.py @@ -95,7 +95,7 @@ def setup_logger(level="", log_file=""): @option('--short-term-suffix', '--short-suffix', 'short_term_suffix', help="The suffix appended to the profile name to" "identify the short term credential section") -@option('--assume-role', '--assume', +@option('--assume-role', '--assume', 'role', metavar='arn:aws:iam::123456788990:role/RoleName', help="The ARN of the AWS IAM Role you would like to " "assume, if specified. This value can also be provided" @@ -127,7 +127,7 @@ def main(device: str, profile: str = "default", long_term_suffix: str = "long-term", short_term_suffix: str = "", - assume_role: str = "", + role: str = "", role_session_name: str = USER, force: bool = False, log_level: str = LOG_LEVEL, @@ -169,7 +169,7 @@ def main(device: str, profile, long_term_suffix, short_term_suffix, - assume_role, + role, keychain, device, duration, @@ -184,15 +184,14 @@ def validate(log: logging.Logger, long_term_suffix: str = "", short_term_suffix: str = "", role_session_name: str = "", - assume_role: bool = False, + role: str = "", keychain: bool = False, device: str = "", duration: int = 0, token: str = "", - force: bool = False - ): + force: bool = False): """ - validate all the options + validate all the options passed into the cli or main function """ # check profile @@ -218,8 +217,8 @@ def validate(log: logging.Logger, "be equal to the value for '--short-term-suffix'") # check assume role - if assume_role: - role_msg = f"with assumed role: {assume_role}" + if role: + role_msg = f"with assumed role: {role}" elif credentials_obj.has_option(profile, 'assumed_role_arn'): role_msg = f"with assumed role: {credentials_obj.get(profile, 'assumed_role_arn')}" else: @@ -266,19 +265,19 @@ def validate(log: logging.Logger, 'You must provide --device or MFA_DEVICE or set ' '"aws_mfa_device" in ".aws/credentials"') - # get assume_role from param or env var - if not assume_role: + # get role from param or env var + if not role: if environ.get('MFA_ASSUME_ROLE'): - assume_role = environ.get('MFA_ASSUME_ROLE') + role = environ.get('MFA_ASSUME_ROLE') elif credentials_obj.has_option(long_term_name, 'assume_role'): - assume_role = credentials_obj.get(long_term_name, 'assume_role') + role = credentials_obj.get(long_term_name, 'assume_role') # get duration from param, env var or set default if not duration: if environ.get('MFA_STS_DURATION'): duration = int(environ.get('MFA_STS_DURATION')) else: - duration = 3600 if assume_role else 43200 + duration = 3600 if role else 43200 # If this is False, only refresh credentials if expired. Otherwise # always refresh. @@ -327,21 +326,21 @@ def validate(log: logging.Logger, force_refresh = True # There are not credentials for an assumed role, # but the user is trying to assume one - elif current_role is None and assume_role: + elif current_role is None and role: log.info(reup_message) force_refresh = True # There are current credentials for a role and # the role arn being provided is the same. - elif current_role is not None and assume_role and current_role == assume_role: + elif current_role is not None and role and current_role == role: pass # There are credentials for a current role and the role # that is attempting to be assumed is different - elif current_role is not None and assume_role and current_role != assume_role: + elif current_role is not None and role and current_role != role: log.info(reup_message) force_refresh = True # There are credentials for a current role and no role arn is # being supplied - elif current_role is not None and assume_role is None: + elif current_role is not None and role is None: log.info(reup_message) force_refresh = True @@ -370,7 +369,7 @@ def validate(log: logging.Logger, token, device, duration, - assume_role, + role, short_term_suffix, role_session_name, region) @@ -384,14 +383,13 @@ def get_credentials(log: logging.Logger, token: str, device: str, duration: int, - assume_role: str, + role: str, short_term_suffix: str, role_session_name: str = "", region: str = ""): """ - Get credentials from AWS? + Get short term credentials """ - if token: log.debug("Received token as argument") mfa_token = str(token) @@ -407,17 +405,17 @@ def get_credentials(log: logging.Logger, aws_secret_access_key=lt_access_key ) - if assume_role: + if role: log.info("Assuming Role - Profile: %s, Role: %s, Duration: %s", - short_term_name, assume_role, duration) + short_term_name, role, duration) if not role_session_name: log_error_and_exit(log, "You must specify a role session name " "via --role-session-name") try: response = client.assume_role( - RoleArn=assume_role, + RoleArn=role, RoleSessionName=role_session_name, DurationSeconds=duration, SerialNumber=device, @@ -430,7 +428,7 @@ def get_credentials(log: logging.Logger, log_error_and_exit(log, "Token must be six digits") credentials_obj.set(short_term_name, 'assumed_role', 'True') - credentials_obj.set(short_term_name, 'assumed_role_arn', assume_role) + credentials_obj.set(short_term_name, 'assumed_role_arn', role) else: log.info(f"Fetching Credentials - Profile: {short_term_name}, Duration: {duration}") try: diff --git a/poetry.lock b/poetry.lock index e7e9155..8382fdd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,18 +1,33 @@ # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +[[package]] +name = "backports-tarfile" +version = "1.2.0" +description = "Backport of CPython tarfile module" +optional = false +python-versions = ">=3.8" +files = [ + {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, + {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] + [[package]] name = "boto3" -version = "1.38.14" +version = "1.38.18" description = "The AWS SDK for Python" optional = false python-versions = ">=3.9" files = [ - {file = "boto3-1.38.14-py3-none-any.whl", hash = "sha256:44bc15285104683cd25dfb60abc5aac65b75d9e79b06f43094d18ed5c2739302"}, - {file = "boto3-1.38.14.tar.gz", hash = "sha256:2cba851374c9b15facd6e7fe3adf7988c216537182d2c139e96da5c101f4cbcf"}, + {file = "boto3-1.38.18-py3-none-any.whl", hash = "sha256:38ecb477ba9fc4edcf97133bf1fe33261ebec6c58d59982abff3cea167624211"}, + {file = "boto3-1.38.18.tar.gz", hash = "sha256:bd723bfbc109bdc63e017ead74dd22f2cf8a7515e24e730870b8a70af823e626"}, ] [package.dependencies] -botocore = ">=1.38.14,<1.39.0" +botocore = ">=1.38.18,<1.39.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.12.0,<0.13.0" @@ -21,13 +36,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.38.14" +version = "1.38.18" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.9" files = [ - {file = "botocore-1.38.14-py3-none-any.whl", hash = "sha256:3125ed92e9ee6137c28fd32c56934a531a372346a7b13cb86de4328d7629e156"}, - {file = "botocore-1.38.14.tar.gz", hash = "sha256:8ac91de6c33651a5c699268f1d22fadd5e99f370230dbea97d29e4164de4e5f2"}, + {file = "botocore-1.38.18-py3-none-any.whl", hash = "sha256:0b5ddf195f15218f30ec63d8aba9e55cf60af2984c068276b9fd206059043310"}, + {file = "botocore-1.38.18.tar.gz", hash = "sha256:ae4c97383e797e9648f8721bb0217fd9efd228e9fbc661d83dc0959be083ec5c"}, ] [package.dependencies] @@ -155,61 +170,61 @@ files = [ [[package]] name = "cryptography" -version = "44.0.3" +version = "45.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" files = [ - {file = "cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88"}, - {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137"}, - {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c"}, - {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76"}, - {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359"}, - {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43"}, - {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01"}, - {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d"}, - {file = "cryptography-44.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904"}, - {file = "cryptography-44.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44"}, - {file = "cryptography-44.0.3-cp37-abi3-win32.whl", hash = "sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d"}, - {file = "cryptography-44.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d"}, - {file = "cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f"}, - {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759"}, - {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645"}, - {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2"}, - {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54"}, - {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93"}, - {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c"}, - {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f"}, - {file = "cryptography-44.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5"}, - {file = "cryptography-44.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b"}, - {file = "cryptography-44.0.3-cp39-abi3-win32.whl", hash = "sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028"}, - {file = "cryptography-44.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334"}, - {file = "cryptography-44.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cad399780053fb383dc067475135e41c9fe7d901a97dd5d9c5dfb5611afc0d7d"}, - {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:21a83f6f35b9cc656d71b5de8d519f566df01e660ac2578805ab245ffd8523f8"}, - {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fc3c9babc1e1faefd62704bb46a69f359a9819eb0292e40df3fb6e3574715cd4"}, - {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:e909df4053064a97f1e6565153ff8bb389af12c5c8d29c343308760890560aff"}, - {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:dad80b45c22e05b259e33ddd458e9e2ba099c86ccf4e88db7bbab4b747b18d06"}, - {file = "cryptography-44.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:479d92908277bed6e1a1c69b277734a7771c2b78633c224445b5c60a9f4bc1d9"}, - {file = "cryptography-44.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:896530bc9107b226f265effa7ef3f21270f18a2026bc09fed1ebd7b66ddf6375"}, - {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9b4d4a5dbee05a2c390bf212e78b99434efec37b17a4bff42f50285c5c8c9647"}, - {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02f55fb4f8b79c1221b0961488eaae21015b69b210e18c386b69de182ebb1259"}, - {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:dd3db61b8fe5be220eee484a17233287d0be6932d056cf5738225b9c05ef4fff"}, - {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:978631ec51a6bbc0b7e58f23b68a8ce9e5f09721940933e9c217068388789fe5"}, - {file = "cryptography-44.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:5d20cc348cca3a8aa7312f42ab953a56e15323800ca3ab0706b8cd452a3a056c"}, - {file = "cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053"}, + {file = "cryptography-45.0.2-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:61a8b1bbddd9332917485b2453d1de49f142e6334ce1d97b7916d5a85d179c84"}, + {file = "cryptography-45.0.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cc31c66411e14dd70e2f384a9204a859dc25b05e1f303df0f5326691061b839"}, + {file = "cryptography-45.0.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:463096533acd5097f8751115bc600b0b64620c4aafcac10c6d0041e6e68f88fe"}, + {file = "cryptography-45.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:cdafb86eb673c3211accffbffdb3cdffa3aaafacd14819e0898d23696d18e4d3"}, + {file = "cryptography-45.0.2-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:05c2385b1f5c89a17df19900cfb1345115a77168f5ed44bdf6fd3de1ce5cc65b"}, + {file = "cryptography-45.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e9e4bdcd70216b08801e267c0b563316b787f957a46e215249921f99288456f9"}, + {file = "cryptography-45.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b2de529027579e43b6dc1f805f467b102fb7d13c1e54c334f1403ee2b37d0059"}, + {file = "cryptography-45.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10d68763892a7b19c22508ab57799c4423c7c8cd61d7eee4c5a6a55a46511949"}, + {file = "cryptography-45.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2a90ce2f0f5b695e4785ac07c19a58244092f3c85d57db6d8eb1a2b26d2aad6"}, + {file = "cryptography-45.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:59c0c8f043dd376bbd9d4f636223836aed50431af4c5a467ed9bf61520294627"}, + {file = "cryptography-45.0.2-cp311-abi3-win32.whl", hash = "sha256:80303ee6a02ef38c4253160446cbeb5c400c07e01d4ddbd4ff722a89b736d95a"}, + {file = "cryptography-45.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7429936146063bd1b2cfc54f0e04016b90ee9b1c908a7bed0800049cbace70eb"}, + {file = "cryptography-45.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:e86c8d54cd19a13e9081898b3c24351683fd39d726ecf8e774aaa9d8d96f5f3a"}, + {file = "cryptography-45.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e328357b6bbf79928363dbf13f4635b7aac0306afb7e5ad24d21d0c5761c3253"}, + {file = "cryptography-45.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49af56491473231159c98c2c26f1a8f3799a60e5cf0e872d00745b858ddac9d2"}, + {file = "cryptography-45.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f169469d04a23282de9d0be349499cb6683b6ff1b68901210faacac9b0c24b7d"}, + {file = "cryptography-45.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9cfd1399064b13043082c660ddd97a0358e41c8b0dc7b77c1243e013d305c344"}, + {file = "cryptography-45.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:18f8084b7ca3ce1b8d38bdfe33c48116edf9a08b4d056ef4a96dceaa36d8d965"}, + {file = "cryptography-45.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2cb03a944a1a412724d15a7c051d50e63a868031f26b6a312f2016965b661942"}, + {file = "cryptography-45.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a9727a21957d3327cf6b7eb5ffc9e4b663909a25fea158e3fcbc49d4cdd7881b"}, + {file = "cryptography-45.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ddb8d01aa900b741d6b7cc585a97aff787175f160ab975e21f880e89d810781a"}, + {file = "cryptography-45.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c0c000c1a09f069632d8a9eb3b610ac029fcc682f1d69b758e625d6ee713f4ed"}, + {file = "cryptography-45.0.2-cp37-abi3-win32.whl", hash = "sha256:08281de408e7eb71ba3cd5098709a356bfdf65eebd7ee7633c3610f0aa80d79b"}, + {file = "cryptography-45.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:48caa55c528617fa6db1a9c3bf2e37ccb31b73e098ac2b71408d1f2db551dde4"}, + {file = "cryptography-45.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8ec324711596fbf21837d3a5db543937dd84597d364769b46e0102250023f77"}, + {file = "cryptography-45.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:965611880c3fa8e504b7458484c0697e00ae6e937279cd6734fdaa2bc954dc49"}, + {file = "cryptography-45.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d891942592789fa0ab71b502550bbadb12f540d7413d7d7c4cef4b02af0f5bc6"}, + {file = "cryptography-45.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b19f4b28dd2ef2e6d600307fee656c00825a2980c4356a7080bd758d633c3a6f"}, + {file = "cryptography-45.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:7c73968fbb7698a4c5d6160859db560d3aac160edde89c751edd5a8bc6560c88"}, + {file = "cryptography-45.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:501de1296b2041dccf2115e3c7d4947430585601b251b140970ce255c5cfb985"}, + {file = "cryptography-45.0.2-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1655d3a76e3dedb683c982a6c3a2cbfae2d08f47a48ec5a3d58db52b3d29ea6f"}, + {file = "cryptography-45.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc7693573f16535428183de8fd27f0ca1ca37a51baa0b41dc5ed7b3d68fe80e2"}, + {file = "cryptography-45.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:614bca7c6ed0d8ad1dce683a6289afae1f880675b4090878a0136c3da16bc693"}, + {file = "cryptography-45.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:4142e20c29224cec63e9e32eb1e6014fb285fe39b7be66b3564ca978a3a8afe9"}, + {file = "cryptography-45.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9a900036b42f7324df7c7ad9569eb92ba0b613cf699160dd9c2154b24fd02f8e"}, + {file = "cryptography-45.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:057723b79752a142efbc609e90b0dff27b0361ccbee3bd48312d70f5cdf53b78"}, + {file = "cryptography-45.0.2.tar.gz", hash = "sha256:d784d57b958ffd07e9e226d17272f9af0c41572557604ca7554214def32c26bf"}, ] [package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} +cffi = {version = ">=1.14", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==44.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==45.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -253,6 +268,29 @@ files = [ [package.extras] license = ["ukkonen"] +[[package]] +name = "importlib-metadata" +version = "8.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +files = [ + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + [[package]] name = "jaraco-classes" version = "3.4.0" @@ -282,6 +320,9 @@ files = [ {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, ] +[package.dependencies] +"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} + [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] @@ -346,6 +387,7 @@ files = [ ] [package.dependencies] +importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} "jaraco.classes" = "*" "jaraco.context" = "*" "jaraco.functools" = "*" @@ -579,6 +621,7 @@ files = [ [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -626,6 +669,17 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] +[[package]] +name = "typing-extensions" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, +] + [[package]] name = "urllib3" version = "2.4.0" @@ -663,7 +717,26 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "zipp" +version = "3.21.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [metadata] lock-version = "2.0" -python-versions = "^3.12" -content-hash = "f814276dd4a56367fd92dcce60b664b2723c2e85d35ab4b839b2f867ef56917f" +python-versions = ">=3.10,<3.13" +content-hash = "d36f68c4185d4159c435de282274ae999e9cc91d0eee36f86c79c119b874e1da" diff --git a/pyproject.toml b/pyproject.toml index ba6a21f..c0f541f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "awth" -version = "0.1.0a6" +version = "0.1.0a7" description = "awth your way into aws, again, with mfa" authors = [ "jessebot ", @@ -21,7 +21,7 @@ packages = [{include = "awth"}] boto3 = "^1.38" click = "^8.1" keyring = "^25.6" -python = "^3.12" +python = ">=3.10,<3.13" rich = "^13.6" secretstorage = "^3.3" From 38a964ddb7c31786aa868f347a1222a1c5b0680e Mon Sep 17 00:00:00 2001 From: jessebot Date: Sun, 18 May 2025 18:27:29 +0200 Subject: [PATCH 2/6] move help text to help.py --- awth/__init__.py | 70 ++++++++++++++--------------------------------- awth/help_text.py | 13 +++++---- 2 files changed, 28 insertions(+), 55 deletions(-) diff --git a/awth/__init__.py b/awth/__init__.py index d1969f1..59e854d 100644 --- a/awth/__init__.py +++ b/awth/__init__.py @@ -68,60 +68,28 @@ def setup_logger(level="", log_file=""): @command(cls=RichCommand) -@option('--device', - metavar='arn:aws:iam::123456788990:mfa/mirandel-smith', - help="The MFA Device ARN. This value can also be " - "provided via the environment variable 'MFA_DEVICE' or" - " the ~/.aws/credentials variable 'aws_mfa_device'.") -@option('--duration', - type=int, - help="The duration, in seconds, that the temporary " - "credentials should remain valid. Minimum value: " - "900 (15 minutes). Maximum: 129600 (36 hours). " - "Defaults to 43200 (12 hours), or 3600 (one " - "hour) when using '--assume-role'. This value " - "can also be provided via the environment " - "variable 'MFA_STS_DURATION'. ") -@option('--profile', - help="If using profiles, specify the name here. The " - "default profile name is 'default'. The value can " - "also be provided via the environment variable " - "'AWS_PROFILE'.", - default="default") +@option('--device', metavar='arn:aws:iam::123456788990:mfa/mirandel-smith', + help=HELP['device']) +@option('--duration', type=int, help=HELP['duration']) +@option('--profile', help=HELP['profile'], default="default") @option('--long-term-suffix', '--long-suffix', 'long_term_suffix', - help="The suffix appended to the profile name to" - "identify the long term credential section", - default="long-term") + help=HELP['long_term_suffix'], default="long-term") @option('--short-term-suffix', '--short-suffix', 'short_term_suffix', - help="The suffix appended to the profile name to" - "identify the short term credential section") + help=HELP['short_term_suffix']) @option('--assume-role', '--assume', 'role', metavar='arn:aws:iam::123456788990:role/RoleName', - help="The ARN of the AWS IAM Role you would like to " - "assume, if specified. This value can also be provided" - " via the environment variable 'MFA_ASSUME_ROLE'") + help=HELP['assume_role']) @option('--role-session-name', "role_session_name", - help="Friendly session name required when using ", - default=USER) -@option('--force', - help="Refresh credentials even if currently valid.") + help=HELP['role_session_name'], default=USER) +@option('--force', help=HELP['force']) @option('--log-level', 'log_level', type=Choice(['CRITICAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'NOTSET'], case_sensitive=False), - help="Set log level", - default=LOG_LEVEL) -@option('--setup', - help="Setup a new log term credentials section", - is_flag=bool) -@option('--token', '--mfa-token', - help="Provide MFA token as an argument", - default="") -@option('--region', - help="AWS STS Region", - type=str) -@option('--keychain', - is_flag=bool, - help="Use system keychain to store or retrieve long term credentials") + help=HELP['log_level'], default=LOG_LEVEL) +@option('--setup', help=HELP['setup'], is_flag=bool) +@option('--token', '--mfa-token', 'token', help=HELP['token'], default="") +@option('--region', help=HELP['region'], type=str) +@option('--keychain', is_flag=bool, help=HELP['keychain']) def main(device: str, duration: int, profile: str = "default", @@ -135,15 +103,17 @@ def main(device: str, token: str = "", region: str = "", keychain: bool = False): + """ + validate the config, get credentials, and assume role if needed + """ # set up logging before we begin logger = setup_logger(log_level) if not path.isfile(AWS_CREDS_PATH): create_credentials_file = Confirm.ask( - "Could not locate credentials file at " - f"[green]{AWS_CREDS_PATH}[/green]. Would you like to create one?" - ) + f"Could not locate credentials file at [green]{AWS_CREDS_PATH}[/]." + "Would you like to create one?") if create_credentials_file: # try creating directory and file @@ -216,7 +186,7 @@ def validate(log: logging.Logger, "The value for '--long-term-suffix' cannot " "be equal to the value for '--short-term-suffix'") - # check assume role + # check AWS role if role: role_msg = f"with assumed role: {role}" elif credentials_obj.has_option(profile, 'assumed_role_arn'): diff --git a/awth/help_text.py b/awth/help_text.py index 7002e8f..633de48 100755 --- a/awth/help_text.py +++ b/awth/help_text.py @@ -56,17 +56,20 @@ def options_help() -> dict: "also be provided via the environment variable " "'AWS_PROFILE'.", - 'long-term-suffix': "The suffix appended to the profile name to" + 'long_term_suffix': "The suffix appended to the profile name to" "identify the long term credential section", - 'short-term-suffix': "The suffix appended to the profile name to" + + 'short_term_suffix': "The suffix appended to the profile name to" "identify the short term credential section", - 'assume-role': "The ARN of the AWS IAM Role you would like to " + + 'assume_role': "The ARN of the AWS IAM Role you would like to " "assume, if specified. This value can also be provided" " via the environment variable 'MFA_ASSUME_ROLE'. " "Example: 'arn:aws:iam::123456788990:role/RoleName'", - 'role-session-name': "Friendly session name required when using --assume-role", + + 'role_session_name': "Friendly session name required when using --assume-role", 'force': "Refresh credentials even if currently valid.", - 'log-level': f"Set log level. {log_levels}", + 'log_level': f"Set log level. {log_levels}", 'setup': "Setup a new log term credentials section", 'token': "Provide MFA token as an argument", 'region': "AWS STS Region", From cf5d5c567feb73ad39d32cf7aaf5bde73664b645 Mon Sep 17 00:00:00 2001 From: jessebot Date: Mon, 19 May 2025 17:37:16 +0200 Subject: [PATCH 3/6] change how we validate region, --- awth/__init__.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/awth/__init__.py b/awth/__init__.py index 59e854d..a216ef5 100644 --- a/awth/__init__.py +++ b/awth/__init__.py @@ -139,6 +139,7 @@ def main(device: str, profile, long_term_suffix, short_term_suffix, + region, role, keychain, device, @@ -154,6 +155,7 @@ def validate(log: logging.Logger, long_term_suffix: str = "", short_term_suffix: str = "", role_session_name: str = "", + region: str = "", role: str = "", keychain: bool = False, device: str = "", @@ -174,7 +176,6 @@ def validate(log: logging.Logger, else: long_term_name = f'{profile}-{long_term_suffix}' - # check short_term_suffix if not short_term_suffix or short_term_suffix.lower() == 'none': short_term_name = profile @@ -241,6 +242,8 @@ def validate(log: logging.Logger, role = environ.get('MFA_ASSUME_ROLE') elif credentials_obj.has_option(long_term_name, 'assume_role'): role = credentials_obj.get(long_term_name, 'assume_role') + elif aws_config_obj.has_option(short_term_name, 'role_arn'): + role = aws_config_obj.get(short_term_name, 'role_arn') # get duration from param, env var or set default if not duration: @@ -249,6 +252,9 @@ def validate(log: logging.Logger, else: duration = 3600 if role else 43200 + if not region and aws_config_obj.has_option(profile, 'region'): + region = aws_config_obj.get(profile, 'region') + # If this is False, only refresh credentials if expired. Otherwise # always refresh. force_refresh = False @@ -330,7 +336,6 @@ def validate(log: logging.Logger, f" they will expire at {exp}") if should_refresh: - region = aws_config_obj.get(profile, 'region') get_credentials(log, credentials_obj, short_term_name, @@ -376,9 +381,9 @@ def get_credentials(log: logging.Logger, ) if role: - - log.info("Assuming Role - Profile: %s, Role: %s, Duration: %s", - short_term_name, role, duration) + log.info(f"Assuming Role - Profile: {short_term_name}, " + f"Role: {role}, " + f"Duration: {duration}") if not role_session_name: log_error_and_exit(log, "You must specify a role session name " "via --role-session-name") @@ -441,11 +446,14 @@ def get_credentials(log: logging.Logger, 'expiration', response['Credentials']['Expiration'].strftime('%Y-%m-%d %H:%M:%S') ) + with open(AWS_CREDS_PATH, 'w') as configfile: credentials_obj.write(configfile) + log.info( f"Success! Your credentials will expire in {duration} seconds at: " f"{response['Credentials']['Expiration']}") + sys.exit(0) From ea0611260f8639cca91afa90bf3bb234afb264e7 Mon Sep 17 00:00:00 2001 From: jessebot Date: Mon, 19 May 2025 22:25:45 +0200 Subject: [PATCH 4/6] add all env vars you can set to README --- README.md | 25 ++++++++++++++++++++++--- awth/__init__.py | 2 +- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1eb68f5..1cfb008 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ pretty usage: screenreader friendly usage: -``` +```help --device arn:aws:iam::123456788990:mfa/mirandel-smith The MFA Device ARN. This value can also be provided via the environment variable 'MFA_DEVICE' or the ~/.aws/credentials variable 'aws_mfa_device'. --duration DURATION The duration, in seconds, that the temporary credentials should remain valid. Minimum value: 900 (15 minutes). Maximum: 129600 (36 hours). Defaults to 43200 (12 hours), or 3600 (one hour) when using '--assume-role'. This value can also be provided via the environment variable 'MFA_STS_DURATION'. @@ -184,13 +184,32 @@ screenreader friendly usage: --short-term-suffix SHORT_TERM_SUFFIX To identify the short term credential section by [-SHORT_TERM_SUFFIX]. Omit or use 'none' to identify the short term credential section by []. ---assume-role arn:aws:iam::123456788990:role/RoleName The ARN of the AWS IAM Role you would like to assume, if specified. This value can also be provided via the environment variable 'MFA_ASSUME_ROLE' +--assume-role --assume arn:aws:iam::123456788990:role/RoleName The ARN of the AWS IAM Role you would like to assume, if specified. This value can also be provided via the environment variable 'MFA_ASSUME_ROLE' --role-session-name ROLE_SESSION_NAME Friendly session name required when using --assume- role. By default, this is your local username. --token TOKEN, --mfa-token TOKEN Provide MFA token as an argument ---no-keychain Do not use system keychain to store or retrieve long term credentials +--keychain Use system keychain to store or retrieve long term credentials + +--region AWS region to authenticate to +``` + +### Optional Environment Variables + +Here are some environment variables you can optionally set instead of passing in arguments or setting parameters in your `~/.aws/*` files. + +```bash +AWS_SHARED_CREDENTIALS_FILE='~/.aws/credentials' +AWS_SHARED_CONFIG_FILE='~/.aws/config' +AWS_PROFILE='default' +MFA_DEVICE='' +MFA_ASSUME_ROLE='' +MFA_STS_DURATION='' +AWS_REGION='eu-central-1' + +# this is only for generating a screenshot of the help menu, used for updating the README in this repo +AWTH_SCREENSHOT='False' ``` **Argument precedence**: Command line arguments take precedence over environment variables. diff --git a/awth/__init__.py b/awth/__init__.py index a216ef5..eba6e35 100644 --- a/awth/__init__.py +++ b/awth/__init__.py @@ -234,7 +234,7 @@ def validate(log: logging.Logger, else: log_error_and_exit(log, 'You must provide --device or MFA_DEVICE or set ' - '"aws_mfa_device" in ".aws/credentials"') + '"aws_mfa_device" in "~/.aws/credentials"') # get role from param or env var if not role: From 30249463c96e59a8459f31a29f06ef6550635e3a Mon Sep 17 00:00:00 2001 From: jessebot Date: Mon, 19 May 2025 22:31:06 +0200 Subject: [PATCH 5/6] commend the env vars --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1cfb008..3835fd6 100644 --- a/README.md +++ b/README.md @@ -200,12 +200,29 @@ screenreader friendly usage: Here are some environment variables you can optionally set instead of passing in arguments or setting parameters in your `~/.aws/*` files. ```bash +# AWS credentails file AWS_SHARED_CREDENTIALS_FILE='~/.aws/credentials' + +# AWS config file AWS_SHARED_CONFIG_FILE='~/.aws/config' + +# AWS Credentials +AWS_ACCESS_KEY_ID="" +AWS_SECRET_ACCESS_KEY="" + +# which AWS profile to use ~/.aws/config AWS_PROFILE='default' + +# the arn of your MFA device in AWS MFA_DEVICE='' + +# this would be something like arn:aws:iam::123456788990:role/RoleName MFA_ASSUME_ROLE='' -MFA_STS_DURATION='' + +# duration to keep valid token for your assumed role +MFA_STS_DURATION='3600' + +# default env var to use AWS_REGION='eu-central-1' # this is only for generating a screenshot of the help menu, used for updating the README in this repo From 0b2ee3fbfd27356daf95b9740eabf5e3e580d122 Mon Sep 17 00:00:00 2001 From: jessebot Date: Tue, 20 May 2025 09:11:50 +0200 Subject: [PATCH 6/6] always check default-long-term for credentials, expect the word 'profile' before short term names in the config file --- awth/__init__.py | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/awth/__init__.py b/awth/__init__.py index eba6e35..6193e71 100644 --- a/awth/__init__.py +++ b/awth/__init__.py @@ -171,7 +171,7 @@ def validate(log: logging.Logger, profile = environ.get('AWS_PROFILE', 'default') # check long_term_suffix - if long_term_suffix.lower() == 'none': + if not long_term_suffix or long_term_suffix.lower() == 'none': long_term_name = profile else: long_term_name = f'{profile}-{long_term_suffix}' @@ -208,29 +208,32 @@ def validate(log: logging.Logger, # if using the AWS credentials file to store credentials else: - try: + # first check the default long term name for the long term credentials + if credentials_obj.has_section(long_term_name): log.info(f"Checking {AWS_CREDS_PATH} for AWS {long_term_name} credentials...") key_id = credentials_obj.get(long_term_name, 'aws_access_key_id') - log.debug(f"key id: {key_id}") access_key = credentials_obj.get(long_term_name, 'aws_secret_access_key') - log.debug(f"access_key: {access_key}") device = credentials_obj.get(long_term_name, 'aws_mfa_device') log.debug(f"device: {device}") - except NoSectionError: + # then check literally [default-long-term] for the long term credentials + elif credentials_obj.has_section(f"default-{long_term_suffix}"): + log.info(f"Checking {AWS_CREDS_PATH} for AWS default-{long_term_suffix} credentials...") + key_id = credentials_obj.get(f"default-{long_term_suffix}", 'aws_access_key_id') + access_key = credentials_obj.get(f"default-{long_term_suffix}", 'aws_secret_access_key') + device = credentials_obj.get(f"default-{long_term_suffix}", 'aws_mfa_device') + log.debug(f"device: {device}") + else: log_error_and_exit(log, - f"Long term credentials session '{long_term_name}' is missing. " - "You must add this section to your credentials file " - "along with your long term 'aws_access_key_id' and " - "'aws_secret_access_key'") - except NoOptionError as e: - log_error_and_exit(log, e) - - # get device from param, env var or config + f"Long term credentials session '{long_term_name}' and " + f"default-{long_term_suffix} are missing. You must add one " + "of these sections to your credentials file along with your " + "long term 'aws_access_key_id' and 'aws_secret_access_key'") + + # get device from param, env var + log.debug("Checking for AWS MFA Device...") if not device: if environ.get('MFA_DEVICE'): device = environ.get('MFA_DEVICE') - elif credentials_obj.has_option(long_term_name, 'aws_mfa_device'): - device = credentials_obj.get(long_term_name, 'aws_mfa_device') else: log_error_and_exit(log, 'You must provide --device or MFA_DEVICE or set ' @@ -243,14 +246,17 @@ def validate(log: logging.Logger, elif credentials_obj.has_option(long_term_name, 'assume_role'): role = credentials_obj.get(long_term_name, 'assume_role') elif aws_config_obj.has_option(short_term_name, 'role_arn'): - role = aws_config_obj.get(short_term_name, 'role_arn') + role = aws_config_obj.get(f'profile {short_term_name}', 'role_arn') # get duration from param, env var or set default if not duration: if environ.get('MFA_STS_DURATION'): duration = int(environ.get('MFA_STS_DURATION')) else: - duration = 3600 if role else 43200 + if role: + duration = 3600 + else: + duration = 43200 if not region and aws_config_obj.has_option(profile, 'region'): region = aws_config_obj.get(profile, 'region')