Skip to content

Commit

Permalink
fix issue with bypassed users
Browse files Browse the repository at this point in the history
Bypassed users precheck result is "allow"
We did not handle this case yet, from now on we accept these users'
connections without any further check

Signed-off-by: Gergely Orosz <gergely.orosz@oneidentity.com>
  • Loading branch information
gorosz committed Apr 3, 2020
1 parent 2476b05 commit 51035b0
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 2 deletions.
19 changes: 17 additions & 2 deletions lib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ def from_config(cls, plugin_configuration, section="duo", second_try=None, http_
def otp_authenticate(self, username, otp):
result = False
try:
self._check_preauth(username)
preauth = self._check_preauth(username)
if self._is_bypass_user(preauth):
return self._log_bypass_and_create_aa_response()

logger.info("Account found, running passcode authentication.")
auth = self._duo.auth(factor="passcode", username=username, passcode=str(otp))
result = self._check_auth_result(auth)
Expand All @@ -112,6 +115,9 @@ def otp_authenticate(self, username, otp):
def push_authenticate(self, username):
try:
preauth = self._check_preauth(username)
if self._is_bypass_user(preauth):
return self._log_bypass_and_create_aa_response()

devices = preauth["devices"]
if not [dev for dev in devices if "push" in dev.get("capabilities", [])]:
raise MFAAuthenticationFailure("No push capable device enrolled.")
Expand All @@ -126,10 +132,19 @@ def push_authenticate(self, username):
raise MFAServiceUnreachable(self._construct_exception_message(e))
return True

def _log_bypass_and_create_aa_response(self):
msg = "User configured as bypass user on Duo."
logger.info(msg)
return AAResponse.accept(reason=msg)

def _is_bypass_user(self, preauth):
result = preauth["result"]
return result == "allow"

def _check_preauth(self, username):
logger.debug("Looking up user.")
preauth = self._duo.preauth(username=username)
if preauth["result"] != "auth":
if preauth["result"] not in ("auth", "allow"):
raise MFAAuthenticationFailure(preauth["status_msg"])
return preauth

Expand Down
5 changes: 5 additions & 0 deletions lib/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ def duo_user(site_parameters):
return site_parameters["username"]


@pytest.fixture
def duo_bypass_user(site_parameters):
return site_parameters["bypass_username"]


@pytest.fixture
def duo_wrong_user(site_parameters):
return site_parameters["wrong_username"]
Expand Down
1 change: 1 addition & 0 deletions lib/tests/site_parameters.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

# Test accounts and passcodes:
; username=
; bypass_username=
wrong_username=nobody
# This is a reusable bypass code, not an OTP actually, used in non-interactive test,
# must be set on Duo service side.
Expand Down
11 changes: 11 additions & 0 deletions lib/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ def test_push_auth_user_decline(client, duo_user, interactive):
assert e.match("Login request denied")


@pytest.mark.interactive
def test_bypass_auth_without_bypass_code_push(client, duo_bypass_user, interactive):
result = client.push_authenticate(duo_bypass_user)
assert result == AAResponse.accept(reason="User configured as bypass user on Duo.")

@pytest.mark.interactive
def test_bypass_auth_without_bypass_code_otp(client, duo_bypass_user, interactive):
otp = interactive.askforinput("Please enter OTP whatever you like")
result = client.otp_authenticate(duo_bypass_user, otp)
assert result == AAResponse.accept(reason="User configured as bypass user on Duo.")

@patch("lib.client.Auth")
def test_push_auth_timeout(patcher, duo_user, interactive):
with pytest.raises(MFAAuthenticationFailure) as e:
Expand Down

0 comments on commit 51035b0

Please sign in to comment.