From 7dd7e621b7c091f475d37f5c12c2e4d4e3ce3edf Mon Sep 17 00:00:00 2001 From: pcmxgti <16561338+pcmxgti@users.noreply.github.com> Date: Mon, 30 Oct 2023 09:48:59 -0400 Subject: [PATCH] Clean up method names for readability --- tests/unit/test_duo.py | 94 ++++++++++++++++++++--------------------- tests/unit/test_okta.py | 6 +-- tokendito/duo.py | 42 +++++++++--------- tokendito/okta.py | 2 +- 4 files changed, 72 insertions(+), 72 deletions(-) diff --git a/tests/unit/test_duo.py b/tests/unit/test_duo.py index e01c18c3..efb95b07 100644 --- a/tests/unit/test_duo.py +++ b/tests/unit/test_duo.py @@ -19,10 +19,10 @@ def test_get_passcode(mocker): assert duo.get_passcode("pytest") is None -def test_prepare_duo_info(): +def test_prepare_info(): """Test behaviour empty return duo info.""" from tokendito.config import config - from tokendito.duo import prepare_duo_info + from tokendito.duo import prepare_info selected_okta_factor = { "_embedded": { @@ -55,17 +55,17 @@ def test_prepare_duo_info(): "sid": "", "version": "3.7", } - assert prepare_duo_info(selected_okta_factor) == expected_duo_info + assert prepare_info(selected_okta_factor) == expected_duo_info with pytest.raises(SystemExit) as err: - prepare_duo_info({"badresponse": "FAIL"}) + prepare_info({"badresponse": "FAIL"}) assert err.value.code == 1 -def test_get_duo_sid(mocker): +def test_get_sid(mocker): """Check if got sid correct.""" from tokendito.config import config - from tokendito.duo import get_duo_sid + from tokendito.duo import get_sid test_duo_info = { "okta_factor": "okta_factor", @@ -84,16 +84,16 @@ def test_get_duo_sid(mocker): duo_api_response = Mock() duo_api_response.url = test_url - mocker.patch("tokendito.duo.duo_api_post", return_value=duo_api_response) + mocker.patch("tokendito.duo.api_post", return_value=duo_api_response) - duo_sid_info, duo_auth_response = get_duo_sid(test_duo_info) + duo_sid_info, duo_auth_response = get_sid(test_duo_info) assert duo_sid_info["sid"] == "testval" assert duo_auth_response.url == test_url - mocker.patch("tokendito.duo.duo_api_post", return_value="FAIL") + mocker.patch("tokendito.duo.api_post", return_value="FAIL") with pytest.raises(SystemExit) as err: - get_duo_sid(test_duo_info) + get_sid(test_duo_info) assert err.value.code == 2 @@ -119,9 +119,9 @@ def test_get_mfa_response(): assert err.value.code == 1 -def test_duo_api_post(mocker): +def test_api_post(mocker): """Test if duo api post correctly.""" - from tokendito.duo import duo_api_post + from tokendito.duo import api_post mock_post = mocker.patch("requests.Session.post") mock_resp = mocker.Mock() @@ -129,13 +129,13 @@ def test_duo_api_post(mocker): mock_resp.json.return_value = {"status": "pytest"} mock_post.return_value = mock_resp - response = duo_api_post("https://pytest/") + response = api_post("https://pytest/") assert response == {"status": "pytest"} -def test_get_duo_devices(mocker): +def test_get_devices(mocker): """Test that we can get a list of devices.""" - from tokendito.duo import get_duo_devices + from tokendito.duo import get_devices mock_resp = mocker.Mock() mock_resp.status_code = 200 @@ -143,7 +143,7 @@ def test_get_duo_devices(mocker): # Test generic failure or empty response with pytest.raises(SystemExit) as err: - get_duo_devices(mock_resp) + get_devices(mock_resp) assert err.value.code == 2 # Test no devices in list @@ -152,7 +152,7 @@ def test_get_duo_devices(mocker): """ - assert get_duo_devices(mock_resp) == [] + assert get_devices(mock_resp) == [] # Test devices in list mock_resp.content = """ @@ -163,46 +163,46 @@ def test_get_duo_devices(mocker): """ - assert get_duo_devices(mock_resp) == [ + assert get_devices(mock_resp) == [ {"device": "pytest_device - pytest_device_name", "factor": "factor_type"} ] -def test_parse_duo_mfa_challenge(): +def test_parse_mfa_challenge(): """Test parsing the response to the challenge.""" - from tokendito.duo import parse_duo_mfa_challenge + from tokendito.duo import parse_mfa_challenge mfa_challenge = Mock() # Test successful challenge mfa_challenge.json = Mock(return_value={"stat": "OK", "response": {"txid": "pytest"}}) - assert parse_duo_mfa_challenge(mfa_challenge) == "pytest" + assert parse_mfa_challenge(mfa_challenge) == "pytest" # Test error mfa_challenge.json = Mock(return_value={"stat": "OK", "response": "error"}) with pytest.raises(SystemExit) as err: - parse_duo_mfa_challenge(mfa_challenge) + parse_mfa_challenge(mfa_challenge) assert err.value.code == 1 # Test no response in returned content mfa_challenge.json = Mock(return_value={"stat": "OK", "badresponse": "error"}) with pytest.raises(SystemExit) as err: - parse_duo_mfa_challenge(mfa_challenge) + parse_mfa_challenge(mfa_challenge) assert err.value.code == 1 # Test API failure mfa_challenge.json = Mock(return_value={"stat": "fail", "response": {"txid": "error"}}) with pytest.raises(SystemExit) as err: - parse_duo_mfa_challenge(mfa_challenge) + parse_mfa_challenge(mfa_challenge) assert err.value.code == 1 -def test_duo_mfa_challenge(mocker): +def test_mfa_challenge(mocker): """TODO: Test MFA challenge.""" - from tokendito.duo import duo_mfa_challenge + from tokendito.duo import mfa_challenge with pytest.raises(SystemExit) as err: - duo_mfa_challenge(None, None, None) + mfa_challenge(None, None, None) assert err.value.code == 2 duo_info = { @@ -223,9 +223,9 @@ def test_duo_mfa_challenge(mocker): duo_api_response = mocker.Mock() duo_api_response.json.return_value = {"stat": "OK", "response": {"txid": "pytest_txid"}} - mocker.patch("tokendito.duo.duo_api_post", return_value=duo_api_response) + mocker.patch("tokendito.duo.api_post", return_value=duo_api_response) - txid = duo_mfa_challenge(duo_info, mfa_option, passcode) + txid = mfa_challenge(duo_info, mfa_option, passcode) assert txid == "pytest_txid" @@ -249,12 +249,12 @@ def test_parse_challenge(): (("failure", "pytest"), None, SystemExit), ], ) -def test_duo_mfa_verify(mocker, return_value, side_effect, expected): +def test_mfa_verify(mocker, return_value, side_effect, expected): """Test MFA challenge completion. side_effect is utilized to return different values on different iterations. """ - from tokendito.duo import duo_mfa_verify + from tokendito.duo import mfa_verify mocker.patch.object(HTTP_client, "post", return_value=None) mocker.patch("time.sleep", return_value=None) @@ -269,16 +269,16 @@ def test_duo_mfa_verify(mocker, return_value, side_effect, expected): if expected == SystemExit: # Test failure as exit condition with pytest.raises(expected) as err: - duo_mfa_verify(duo_info, txid) + mfa_verify(duo_info, txid) assert err.value.code == 2 else: # Test success, failure, and iterated calls - assert duo_mfa_verify(duo_info, txid) == expected + assert mfa_verify(duo_info, txid) == expected -def test_duo_factor_callback(mocker): +def test_factor_callback(mocker): """Test submitting factor to callback API.""" - from tokendito.duo import duo_factor_callback + from tokendito.duo import factor_callback duo_info = {"host": "pytest_host", "sid": "pytest_sid", "tile_sig": "pytest_tile_sig"} verify_mfa = {"result_url": "/pytest_result_url"} @@ -288,25 +288,25 @@ def test_duo_factor_callback(mocker): "stat": "OK", "response": {"txid": "pytest_txid", "cookie": "pytest_cookie"}, } - mocker.patch("tokendito.duo.duo_api_post", return_value=duo_api_response) + mocker.patch("tokendito.duo.api_post", return_value=duo_api_response) # Test successful retrieval of the cookie - sig_response = duo_factor_callback(duo_info, verify_mfa) + sig_response = factor_callback(duo_info, verify_mfa) assert sig_response == "pytest_cookie:pytest_tile_sig" # Test failure to retrieve the cookie duo_api_response.json.return_value = {"stat": "FAIL", "response": "pytest_error"} with pytest.raises(SystemExit) as err: - duo_factor_callback(duo_info, verify_mfa) + factor_callback(duo_info, verify_mfa) assert err.value.code == 2 -def test_authenticate_duo(mocker): +def test_authenticate(mocker): """Test end to end authentication.""" - from tokendito.duo import authenticate_duo + from tokendito.duo import authenticate mocker.patch( - "tokendito.duo.get_duo_sid", + "tokendito.duo.get_sid", return_value=( { "sid": "pytest", @@ -320,13 +320,13 @@ def test_authenticate_duo(mocker): ) # We mock a lot of functions here, but we're really just testing that the data can flow, # and that it can be parsed correctly to be sent to the API endpoint. - mocker.patch("tokendito.duo.get_duo_devices", return_value=[{"device": "pytest - device"}]) + mocker.patch("tokendito.duo.get_devices", return_value=[{"device": "pytest - device"}]) mocker.patch("tokendito.user.select_preferred_mfa_index", return_value=0) mocker.patch("tokendito.user.input", return_value="0123456") - mocker.patch("tokendito.duo.duo_mfa_challenge", return_value="txid_pytest") - mocker.patch("tokendito.duo.duo_mfa_verify", return_value={"result_url": "/pytest_result_url"}) - mocker.patch("tokendito.duo.duo_api_post", return_value=None) - mocker.patch("tokendito.duo.duo_factor_callback", return_value="pytest_cookie:pytest_tile_sig") + mocker.patch("tokendito.duo.mfa_challenge", return_value="txid_pytest") + mocker.patch("tokendito.duo.mfa_verify", return_value={"result_url": "/pytest_result_url"}) + mocker.patch("tokendito.duo.api_post", return_value=None) + mocker.patch("tokendito.duo.factor_callback", return_value="pytest_cookie:pytest_tile_sig") selected_okta_factor = { "_embedded": { "factor": { @@ -346,7 +346,7 @@ def test_authenticate_duo(mocker): "stateToken": 12345, } - res = authenticate_duo(selected_okta_factor) + res = authenticate(selected_okta_factor) assert { "id": "pytest", "sig_response": "pytest_cookie:pytest_tile_sig", diff --git a/tests/unit/test_okta.py b/tests/unit/test_okta.py index 1d322b7d..7162b1d5 100644 --- a/tests/unit/test_okta.py +++ b/tests/unit/test_okta.py @@ -89,7 +89,7 @@ def test_mfa_provider_type( mock_response = {"sessionToken": session_token} mocker.patch.object(HTTP_client, "post", return_value=mock_response) - mocker.patch("tokendito.duo.duo_api_post", return_value=None) + mocker.patch("tokendito.duo.api_post", return_value=None) payload = {"x": "y", "t": "z"} selected_mfa_option = 1 @@ -97,7 +97,7 @@ def test_mfa_provider_type( primary_auth = 1 pytest_config = Config() - mocker.patch("tokendito.duo.authenticate_duo", return_value=payload) + mocker.patch("tokendito.duo.authenticate", return_value=payload) mocker.patch("tokendito.okta.push_approval", return_value={"sessionToken": session_token}) mocker.patch("tokendito.okta.totp_approval", return_value={"sessionToken": session_token}) @@ -135,7 +135,7 @@ def test_bad_mfa_provider_type(mocker, sample_headers): mock_response = Mock() mock_response.json.return_value = mfa_verify - mocker.patch("tokendito.duo.authenticate_duo", return_value=payload) + mocker.patch("tokendito.duo.authenticate", return_value=payload) mocker.patch.object(HTTP_client, "post", return_value=mock_response) mocker.patch("tokendito.okta.totp_approval", return_value=mfa_verify) diff --git a/tokendito/duo.py b/tokendito/duo.py index ba3b25c2..3853dcbd 100644 --- a/tokendito/duo.py +++ b/tokendito/duo.py @@ -17,7 +17,7 @@ logger = logging.getLogger(__name__) -def prepare_duo_info(selected_okta_factor): +def prepare_info(selected_okta_factor): """Aggregate most of the parameters needed throughout the Duo authentication process. :param selected_okta_factor: dict response describing Duo factor in Okta. @@ -45,7 +45,7 @@ def prepare_duo_info(selected_okta_factor): return duo_info -def duo_api_post(url, params=None, headers=None, payload=None): +def api_post(url, params=None, headers=None, payload=None): """Error handling and response parsing wrapper for Duo POSTs. :param url: The URL being connected to. @@ -58,7 +58,7 @@ def duo_api_post(url, params=None, headers=None, payload=None): return response -def get_duo_sid(duo_info): +def get_sid(duo_info): """Perform the initial Duo authentication request to obtain the SID. The SID is referenced throughout the authentication process for Duo. @@ -71,7 +71,7 @@ def get_duo_sid(duo_info): url = f"https://{duo_info['host']}/frame/web/v1/auth" logger.debug(f"Calling Duo {urlparse(url).path} with params {params.keys()}") - duo_auth_response = duo_api_post(url, params=params) + duo_auth_response = api_post(url, params=params) try: duo_auth_redirect = urlparse(f"{unquote(duo_auth_response.url)}").query @@ -83,7 +83,7 @@ def get_duo_sid(duo_info): return (duo_info, duo_auth_response) -def get_duo_devices(duo_auth): +def get_devices(duo_auth): """Parse Duo auth response to extract user's MFA options. The /frame/web/v1/auth API returns an html page that lists @@ -117,7 +117,7 @@ def get_duo_devices(duo_auth): return factor_options -def parse_duo_mfa_challenge(mfa_challenge): +def parse_mfa_challenge(mfa_challenge): """Gracefully parse Duo MFA challenge response. :param mfa_challenge: Duo API response for MFA challenge. @@ -141,7 +141,7 @@ def parse_duo_mfa_challenge(mfa_challenge): return txid -def duo_mfa_challenge(duo_info, mfa_option, passcode): +def mfa_challenge(duo_info, mfa_option, passcode): """Poke Duo to challenge the selected factor. After the user has selected their device and factor of choice, @@ -171,8 +171,8 @@ def duo_mfa_challenge(duo_info, mfa_option, passcode): if passcode: mfa_data["passcode"] = passcode - mfa_challenge = duo_api_post(url, payload=mfa_data) - txid = parse_duo_mfa_challenge(mfa_challenge) + mfa_challenge = api_post(url, payload=mfa_data) + txid = parse_mfa_challenge(mfa_challenge) logger.debug(f"Sent MFA Challenge and obtained Duo transaction ID {txid}") return txid @@ -219,7 +219,7 @@ def parse_challenge(verify_mfa, challenge_result): return (challenge_result, challenge_reason) -def duo_mfa_verify(duo_info, txid): +def mfa_verify(duo_info, txid): """Verify MFA challenge completion. After the user has received the MFA challenge, query the Duo API @@ -235,7 +235,7 @@ def duo_mfa_verify(duo_info, txid): while True: logger.debug("Waiting for MFA challenge response") - mfa_result = duo_api_post(url, payload=challenged_mfa) + mfa_result = api_post(url, payload=challenged_mfa) verify_mfa = get_mfa_response(mfa_result) (challenge_result, challenge_reason) = parse_challenge(verify_mfa, challenge_result) @@ -252,7 +252,7 @@ def duo_mfa_verify(duo_info, txid): return verify_mfa -def duo_factor_callback(duo_info, verify_mfa): +def factor_callback(duo_info, verify_mfa): """Inform factor callback api of successful challenge. This request seems to inform this factor's callback url @@ -263,7 +263,7 @@ def duo_factor_callback(duo_info, verify_mfa): :return sig_response: required to sign final Duo callback request. """ factor_callback_url = f"https://{duo_info['host']}{verify_mfa['result_url']}" - factor_callback = duo_api_post(factor_callback_url, payload={"sid": duo_info["sid"]}) + factor_callback = api_post(factor_callback_url, payload={"sid": duo_info["sid"]}) try: sig_response = f"{factor_callback.json()['response']['cookie']}:{duo_info['tile_sig']}" @@ -295,7 +295,7 @@ def get_passcode(mfa_option): return passcode -def authenticate_duo(selected_okta_factor): +def authenticate(selected_okta_factor): """Accomplish MFA via Duo. This is the main function that coordinates the Duo @@ -305,10 +305,10 @@ def authenticate_duo(selected_okta_factor): :param selected_okta_factor: Duo factor information retrieved from Okta. :return payload: required payload for Okta callback """ - duo_info = prepare_duo_info(selected_okta_factor) + duo_info = prepare_info(selected_okta_factor) # Collect devices, factors, auth params for Duo - (duo_info, duo_auth_response) = get_duo_sid(duo_info) - factor_options = get_duo_devices(duo_auth_response) + (duo_info, duo_auth_response) = get_sid(duo_info) + factor_options = get_devices(duo_auth_response) mfa_index = user.select_preferred_mfa_index( factor_options, factor_key="factor", subfactor_key="device" ) @@ -317,11 +317,11 @@ def authenticate_duo(selected_okta_factor): logger.debug(f"Selected MFA is [{mfa_option}]") passcode = get_passcode(mfa_option) - txid = duo_mfa_challenge(duo_info, mfa_option, passcode) - verify_mfa = duo_mfa_verify(duo_info, txid) + txid = mfa_challenge(duo_info, mfa_option, passcode) + verify_mfa = mfa_verify(duo_info, txid) # Make factor callback to Duo - sig_response = duo_factor_callback(duo_info, verify_mfa) + sig_response = factor_callback(duo_info, verify_mfa) # Prepare for Okta callback payload = { @@ -331,5 +331,5 @@ def authenticate_duo(selected_okta_factor): } # Send Okta callback and return payload - duo_api_post(duo_info["okta_callback_url"], payload=payload) + api_post(duo_info["okta_callback_url"], payload=payload) return payload diff --git a/tokendito/okta.py b/tokendito/okta.py index 9c33b4e4..1fa249bd 100644 --- a/tokendito/okta.py +++ b/tokendito/okta.py @@ -464,7 +464,7 @@ def mfa_provider_type( factor_type = selected_factor.get("_embedded", {}).get("factor", {}).get("factorType", None) if mfa_provider == "DUO": - mfa_verify = duo.authenticate_duo(selected_factor) + mfa_verify = duo.authenticate(selected_factor) headers = {"content-type": "application/json", "accept": "application/json"} mfa_verify = HTTP_client.post( mfa_challenge_url, json=payload, headers=headers, return_json=True