Skip to content

Commit

Permalink
Change mfa method from strict match to partial match. (#83)
Browse files Browse the repository at this point in the history
This allows for passing an mfa id, or a more specific mfa method
Also adds Python 3.11 support
  • Loading branch information
pcmxgti committed Oct 31, 2022
1 parent 6a29fc3 commit deb7601
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 30 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down Expand Up @@ -72,14 +72,14 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox
- name: Run Authenticated tests
run: |
tox -e py310,auth
tox -e py311,auth
env:
TOKENDITO_OKTA_USERNAME: ${{ secrets.TOXTEST_OKTA_USERNAME }}
TOKENDITO_OKTA_PASSWORD: ${{ secrets.TOXTEST_OKTA_PASSWORD }}
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[tool.black]
line-length = 100
target-version = ['py36', 'py37', 'py38', 'py39', 'py310']
target-version = ['py36', 'py37', 'py38', 'py39', 'py310', 'py311']
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
keywords=["okta", "aws", "sts"],
packages=find_packages(exclude=["contrib", "docs", "tests", ".tox"]),
Expand Down
12 changes: 12 additions & 0 deletions tests/okta_response_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@
"provider": "OKTA",
"profile": {"credentialId": "First.Last@acme.org"},
},
{
"id": "fdsfsd6ewREr0",
"factorType": "pytest_dupe",
"provider": "GOOGLE",
"profile": {"credentialId": "First.Last@acme.org"},
},
{
"id": "fdsfsd6ewREr1",
"factorType": "pytest_dupe",
"provider": "OKTA",
"profile": {"credentialId": "First.Last@acme.org"},
},
],
},
}
Expand Down
20 changes: 15 additions & 5 deletions tests/unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,18 +702,26 @@ def test_mfa_option_info(factor_type, output):
assert mfa_option_info(mfa_option) == output


@pytest.mark.parametrize("preset_mfa, output", [("push", 0), (None, 1)])
@pytest.mark.parametrize(
"preset_mfa, output",
[("push", 0), (None, 1), ("0xdeadbeef", 1), ("opfrar9yi4bKJNH2WEWQ0x8", 0), ("pytest_dupe", 1)],
)
def test_user_mfa_index(preset_mfa, output, mocker, sample_json_response):
"""Test whether the function returns correct mfa method index."""
from tokendito.okta import user_mfa_index

primary_auth = sample_json_response["okta_response_mfa"]

mfa_options = primary_auth["_embedded"]["factors"]
available_mfas = [d["factorType"] for d in mfa_options]
available_mfas = [f"{d['provider']}_{d['factorType']}_{d['id']}" for d in mfa_options]
mocker.patch("tokendito.user.select_preferred_mfa_index", return_value=1)

assert user_mfa_index(preset_mfa, available_mfas, mfa_options) == output
if preset_mfa == "pytest_dupe":
with pytest.raises(SystemExit) as err:
user_mfa_index(preset_mfa, available_mfas, mfa_options)
assert err.value.code == output
else:
assert user_mfa_index(preset_mfa, available_mfas, mfa_options) == output


def test_select_preferred_mfa_index(mocker, sample_json_response):
Expand Down Expand Up @@ -745,6 +753,8 @@ def test_select_preferred_mfa_index_output(email, capsys, mocker, sample_json_re
"[0] OKTA push Redmi 6 Pro Id: opfrar9yi4bKJNH2WEWQ0x8\n"
f"[1] GOOGLE token:software:totp {email} Id: FfdskljfdsS1ljUT0r8\n"
f"[2] OKTA token:software:totp {email} Id: fdsfsd6ewREr8\n"
f"[3] GOOGLE pytest_dupe Not Presented Id: fdsfsd6ewREr0\n"
f"[4] OKTA pytest_dupe Not Presented Id: fdsfsd6ewREr1\n"
)

mocker.patch("tokendito.user.collect_integer", return_value=1)
Expand Down Expand Up @@ -1014,8 +1024,8 @@ def test_get_submodules_names():
ret = user.get_submodule_names()
assert "__main__" in ret

ret = user.get_submodule_names("")
assert ret == []
with pytest.raises(TypeError):
user.get_submodule_names("")


def test_process_interactive_input(mocker):
Expand Down
38 changes: 20 additions & 18 deletions tokendito/okta.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,24 @@ def user_mfa_index(preset_mfa, available_mfas, mfa_options):
:param available_mfas: available mfa method ids
:param mfa_options: available mfa methods
"""
logger.debug("Get mfa method index in request.")
if preset_mfa is not None and preset_mfa in available_mfas:
mfa_index = available_mfas.index(preset_mfa)
else:
indices = []
# Gets the index number from each preset MFA in the list of avaliable ones.
if preset_mfa:
logger.debug(f"Get mfa method from {available_mfas}.")
indices = [i for i, elem in enumerate(available_mfas) if preset_mfa in elem]

mfa_index = None
if len(indices) == 0:
logger.debug(f"No matches with {preset_mfa}, going to get user input")
mfa_index = user.select_preferred_mfa_index(mfa_options)
elif len(indices) == 1:
logger.debug(f"One match: {preset_mfa} in {indices}")
mfa_index = indices[0]
else:
logger.error(
f"{preset_mfa} is not unique in {available_mfas}. Please check your configuration."
)
sys.exit(1)

return mfa_index

Expand All @@ -194,20 +207,9 @@ def user_mfa_challenge(headers, primary_auth):

preset_mfa = config.okta["mfa_method"]

available_mfas = [d["factorType"] for d in mfa_options]

if available_mfas.count(preset_mfa) > 1:
mfa_method = config.okta["mfa_method"]
mfa_index = available_mfas.index(preset_mfa)
provider = mfa_options[mfa_index]["provider"]
mfa_id = mfa_options[mfa_index]["id"]

logger.warning(
f"\n\nMore than one method found with {mfa_method}.\n"
f"Defaulting to {provider} - {mfa_method} - Id: {mfa_id}.\n"
"This functionality will be deprecated in"
"the next major release.\n"
)
# This creates a list where each elements looks like provider_factor_id.
# For example, OKTA_push_9yi4bKJNH2WEWQ0x8, GOOGLE_token:software:totp_9yi4bKJNH2WEWQ
available_mfas = [f"{d['provider']}_{d['factorType']}_{d['id']}" for d in mfa_options]

mfa_index = user_mfa_index(preset_mfa, available_mfas, mfa_options)

Expand Down
4 changes: 2 additions & 2 deletions tokendito/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def create_directory(dir_name):
sys.exit(1)


def get_submodule_names(location=__file__):
def get_submodule_names():
"""Inspect the current module and find any submodules.
:return: List of submodule names
Expand All @@ -172,7 +172,7 @@ def get_submodule_names(location=__file__):
submodules = []

try:
package = Path(location).resolve(strict=True)
package = Path(__file__).resolve(strict=True)
submodules = [x.name for x in iter_modules([str(package.parent)])]
except Exception as err:
logger.warning(f"Could not resolve modules: {str(err)}")
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
skipsdist = true
envlist = lint, py{36,37,38,39,310}, auth, coverage
envlist = lint, py{36,37,38,39,310,311}, auth, coverage

[testenv]
deps = -r requirements-dev.txt
Expand Down

0 comments on commit deb7601

Please sign in to comment.