Skip to content

Commit

Permalink
Merge pull request privacyidea#3888 from privacyidea/3886/event-condi…
Browse files Browse the repository at this point in the history
…tion

Add result->authentication to the event handler conditions
  • Loading branch information
nilsbehlen authored Mar 26, 2024
2 parents 98bc66d + 9c4e630 commit 4f38e5e
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 10 deletions.
8 changes: 8 additions & 0 deletions doc/eventhandler/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,14 @@ This condition checks the result of an event.
E.g. the result of the event *validate_check* can be a failed authentication.
This can be the trigger to notify either the token owner or the administrator.

**result_authentication**

This checks the entry `result->authentication` in the response.
Possible values are "ACCEPT", "REJECT", "DECLINED" and "CHALLENGE".
It is an enhancement to `result_value`.
If `result_value` is *true*, the `result_authentication` will be "ACCEPT".
If `result_value` is *false*, the `result_authentication` can be "CHALLENGE", "REJECT" or "DELINCED".

**rollout_state**

This is the rollout_state of a token. A token can be rolled out in several steps
Expand Down
15 changes: 14 additions & 1 deletion privacyidea/lib/eventhandler/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from privacyidea.lib.utils import (compare_condition, compare_value_value,
compare_generic_condition,
parse_time_offset_from_now, is_true,
check_ip_in_policy)
check_ip_in_policy, AUTH_RESPONSE)
import datetime
from dateutil.tz import tzlocal
import re
Expand Down Expand Up @@ -72,6 +72,7 @@ class CONDITION(object):
DETAIL_MESSAGE = "detail_message"
RESULT_VALUE = "result_value"
RESULT_STATUS = "result_status"
RESULT_AUTHENTICATION = "result_authentication"
TOKENREALM = "tokenrealm"
TOKENRESOLVER = "tokenresolver"
REALM = "realm"
Expand Down Expand Up @@ -198,6 +199,12 @@ def conditions(cls):
"value": ("True", "False"),
"group": GROUP.GENERAL
},
CONDITION.RESULT_AUTHENTICATION: {
"type": "str",
"desc": _("The result.authentication within the response is the given value."),
"value": (AUTH_RESPONSE.ACCEPT, AUTH_RESPONSE.REJECT, AUTH_RESPONSE.CHALLENGE, AUTH_RESPONSE.DECLINED),
"group": GROUP.GENERAL
},
"token_locked": {
"type": "str",
"desc": _("Check if the max failcounter of the token is "
Expand Down Expand Up @@ -444,6 +451,12 @@ def check_condition(self, options):
if is_true(condition_value) != is_true(result_status):
return False

if CONDITION.RESULT_AUTHENTICATION in conditions:
condition_value = conditions.get(CONDITION.RESULT_AUTHENTICATION)
result_auth = content.get("result", {}).get("authentication")
if condition_value != result_auth:
return False

# checking of max-failcounter state of the token
if "token_locked" in conditions:
if token_obj:
Expand Down
23 changes: 16 additions & 7 deletions privacyidea/lib/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@
"s": string.punctuation} # special


class AUTH_RESPONSE(object):
__doc__ = """The return value in "result->authentication".
CHALLENGE indicates the start of a challenge and
DECLINED indicates a declined challenge, which e.g. happens with PUSH tokens, if the user declines the
authentication request.
"""
ACCEPT = "ACCEPT"
REJECT = "REJECT"
CHALLENGE = "CHALLENGE"
DECLINED = "DECLINED"


def check_time_in_range(time_range, check_time=None):
"""
Check if the given time is contained in the time_range string.
Expand Down Expand Up @@ -1290,19 +1302,16 @@ def prepare_result(obj, rid=1, details=None):
details["threadid"] = threading.current_thread().ident
res["detail"] = details

# Fix for sending an information about challenge response
# TODO: Make this default, when we move from the binary result->value to
# more states in version 4.0
if rid > 1:
if obj:
r_authentication = "ACCEPT"
r_authentication = AUTH_RESPONSE.ACCEPT
elif not obj and details.get("multi_challenge"):
# We have a challenge authentication
r_authentication = "CHALLENGE"
r_authentication = AUTH_RESPONSE.CHALLENGE
elif not obj and (details.get("challenge_status") == "declined"):
r_authentication = "DECLINED"
r_authentication = AUTH_RESPONSE.DECLINED
else:
r_authentication = "REJECT"
r_authentication = AUTH_RESPONSE.REJECT
res["result"]["authentication"] = r_authentication

return res
Expand Down
20 changes: 18 additions & 2 deletions tests/test_lib_eventhandler_usernotification.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from privacyidea.lib.token import init_token, unassign_token, remove_token
from privacyidea.lib.tokenclass import DATE_FORMAT
from privacyidea.lib.user import User, create_user
from privacyidea.lib.utils import to_unicode
from privacyidea.lib.utils import to_unicode, AUTH_RESPONSE
from privacyidea.models import TokenOwner
from . import smtpmock
from .base import MyTestCase, FakeFlaskG, FakeAudit
Expand Down Expand Up @@ -232,7 +232,7 @@ def test_05_check_conditions(self):
uhandler = UserNotificationEventHandler()
resp = Response()
# The actual result_status is false and the result_value is false.
resp.data = """{"result": {"value": false, "status": false}}"""
resp.data = """{"result": {"value": false, "authentication": "REJECT", "status": false}}"""
builder = EnvironBuilder(method='POST')
env = builder.get_environ()
req = Request(env)
Expand All @@ -245,6 +245,22 @@ def test_05_check_conditions(self):
"request": req})
self.assertEqual(r, False)

# We expect the result_authentication to be "REJECT" and it is
r = uhandler.check_condition(
{"g": {},
"handler_def": {"conditions": {"result_authentication": AUTH_RESPONSE.REJECT}},
"response": resp,
"request": req})
self.assertTrue(r)

# We expect the result_authentication to be "ACCEPT" and it is not
r = uhandler.check_condition(
{"g": {},
"handler_def": {"conditions": {"result_authentication": AUTH_RESPONSE.ACCEPT}},
"response": resp,
"request": req})
self.assertFalse(r)

# We expect the result_value to be True, but it is not.
r = uhandler.check_condition(
{"g": {},
Expand Down

0 comments on commit 4f38e5e

Please sign in to comment.