diff --git a/requirements-dev.txt b/requirements-dev.txt index c718ac6..b1dd4af 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ flake8>=5.0.0 flake8-black flake8-docstrings flake8-import-order -moto +moto<5.0.0 pep8-naming pexpect pydocstyle diff --git a/setup.py b/setup.py index 9ddce48..199d7e7 100755 --- a/setup.py +++ b/setup.py @@ -60,6 +60,9 @@ license=about["__license__"], zip_safe=False, install_requires=[required], + extras_require={ + 'inquirer': ['inquirer'], + }, entry_points={ "console_scripts": ["tokendito=tokendito.__main__:main"], }, diff --git a/tests/unit/test_user.py b/tests/unit/test_user.py index b689663..92430c5 100644 --- a/tests/unit/test_user.py +++ b/tests/unit/test_user.py @@ -560,6 +560,9 @@ def test_mfa_option_info(factor_type, output): def test_select_preferred_mfa_index(mocker, sample_json_response): """Test whether the function returns index entered by user.""" + # Don't enable inquirer for these tests + mocker.patch('tokendito.user.INQUIRER_AVAILABLE', False) + from tokendito.user import select_preferred_mfa_index primary_auth = sample_json_response @@ -569,6 +572,20 @@ def test_select_preferred_mfa_index(mocker, sample_json_response): assert select_preferred_mfa_index(mfa_options) == output +def test_select_preferred_mfa_index_inquirer(mocker, sample_json_response): + """Test whether the function returns index entered by user.""" + from tokendito.user import select_preferred_mfa_index + from tokendito.user import INQUIRER_AVAILABLE + + if not INQUIRER_AVAILABLE: + pytest.skip("No items found") + else: + primary_auth = sample_json_response + mfa_options = primary_auth["okta_response_mfa"]["_embedded"]["factors"] + for output in mfa_options: + mocker.patch("tokendito.user.inquirer.prompt", return_value={"mfa_selection": output}) + assert select_preferred_mfa_index(mfa_options) == output + @pytest.mark.parametrize( "email", [ @@ -577,6 +594,9 @@ def test_select_preferred_mfa_index(mocker, sample_json_response): ) def test_select_preferred_mfa_index_output(email, capsys, mocker, sample_json_response): """Test whether the function gives correct output.""" + + # Don't enable inquirer for these tests + mocker.patch('tokendito.user.INQUIRER_AVAILABLE', False) from tokendito.config import config from tokendito.user import select_preferred_mfa_index diff --git a/tokendito/user.py b/tokendito/user.py index be88f39..4015c33 100644 --- a/tokendito/user.py +++ b/tokendito/user.py @@ -34,6 +34,14 @@ except ModuleNotFoundError: pass +INQUIRER_AVAILABLE = False +try: + import inquirer + + INQUIRER_AVAILABLE = True +except ModuleNotFoundError: + pass + logger = logging.getLogger(__name__) mask_items = [] @@ -447,25 +455,41 @@ def select_preferred_mfa_index(mfa_options, factor_key="provider", subfactor_key """ logger.debug("Show all the MFA options to the users.") logger.debug(json.dumps(mfa_options)) - print("\nSelect your preferred MFA method and press Enter:") + longest_index = len(str(len(mfa_options))) longest_factor_name = max([len(d[factor_key]) for d in mfa_options]) longest_subfactor_name = max([len(d[subfactor_key]) for d in mfa_options]) factor_info_indent = max([len(mfa_option_info(d)) for d in mfa_options]) + mfa_list = [] for i, mfa_option in enumerate(mfa_options): factor_id = mfa_option.get("id", "Not presented") factor_info = mfa_option_info(mfa_option) mfa = mfa_option.get(subfactor_key, "Not presented") provider = mfa_option.get(factor_key, "Not presented") - print( + mfa_item = ( f"[{i: >{longest_index}}] " f"{provider: <{longest_factor_name}} " f"{mfa: <{longest_subfactor_name}} " f"{factor_info: <{factor_info_indent}} " f"Id: {factor_id}" ) + mfa_list.append((mfa_item, i)) + + if INQUIRER_AVAILABLE: + questions = [ + inquirer.List('mfa_selection', + message="Select your preferred MFA method and press Enter:", + choices=mfa_list, + ), + ] + answers = inquirer.prompt(questions) + return answers['mfa_selection'] + + print("\nSelect your preferred MFA method and press Enter:") + for text, i in mfa_list: + print(text) user_input = collect_integer(len(mfa_options)) @@ -492,13 +516,13 @@ def prompt_role_choices(aut_tiles): aliases_mapping.append((tile["label"], role.split(":")[4], role, url)) logger.debug("Ask user to select role") - print("\nPlease select one of the following:") longest_alias = max(len(i[1]) for i in aliases_mapping) longest_index = len(str(len(aliases_mapping))) aliases_mapping = sorted(aliases_mapping) print_label = "" + role_list = [] for i, data in enumerate(aliases_mapping): label, alias, role, _ = data padding_index = longest_index - len(str(i)) @@ -506,8 +530,23 @@ def prompt_role_choices(aut_tiles): print_label = label print(f"\n{label}:") - print(f"[{i}] {padding_index * ' '}" f"{alias: <{longest_alias}} {role}") + role_item = f"[{i}] {padding_index * ' '}" f"{alias: <{longest_alias}} {role}" + role_list.append((role_item, (aliases_mapping[i][2], aliases_mapping[i][3]))) + + if INQUIRER_AVAILABLE: + questions = [ + inquirer.List('role_selection', + message="Please select one of the following:", + choices=role_list, + ), + ] + answers = inquirer.prompt(questions) + logger.debug(f"Selected role [{answers.get('role_selection')}]") + return answers['role_selection'] + print("\nPlease select one of the following:") + for role_item, i in role_list: + print(role_item) user_input = collect_integer(len(aliases_mapping)) selected_role = (aliases_mapping[user_input][2], aliases_mapping[user_input][3]) logger.debug(f"Selected role [{user_input}]")