Repository: privacyidea/privacyidea

Issue:
Configuring pre-eventhandler with empty condition makes authentication fail
When adding a pre-eventhandler and activating a condition **without** specifying a value, we'll get an entry in the eventhandlercondition table without a value as well.
If this eventhandler is triggered, it fails with a `IndexError: string index out of range`.
In case of a **post**-eventhandler, the empty condition value is silently ignored and no entry in the eventhandlercondition table is created.
I guess, this is an UI issue, but we should check the the table entries for consistency anyway before using the values (See #1596).


Discussion:
This happens if e.g. the client IP condition is checked but there is a *blank* in the condition.
If the condition is really empty (none or 0 string), then the condition will not be saved.

The DB table would look like this:
~~~~
> select * from eventhandlercondition;
+----+-----------------+--------------+-----------------------------+------------+
| id | eventhandler_id | Key          | Value                       | comparator |
+----+-----------------+--------------+-----------------------------+------------+
|  4 |               4 | client_ip    |                             | equal      |
~~~~

This seems to be an explicit problem of the ip checking.
It will come here:
https://github.com/privacyidea/privacyidea/blob/master/privacyidea/lib/eventhandler/base.py#L354
and enter with a list ``[u'']`` which will make ``check_ip_in_policy`` fail with a string index out of range.

I was not able to create this error for other conditions than the client ip.

Resolving patch:
diff --git a/privacyidea/lib/utils.py b/privacyidea/lib/utils.py
--- a/privacyidea/lib/utils.py
+++ b/privacyidea/lib/utils.py
@@ -650,6 +650,8 @@ def check_ip_in_policy(client_ip, policy):
     """
     client_found = False
     client_excluded = False
+    # Remove empty strings from the list
+    policy = list(filter(None, policy))
     for ipdef in policy:
         if ipdef[0] in ['-', '!']:
             # exclude the client?
@@ -747,24 +749,31 @@ def compare_condition(condition, value):
     :return: True or False
     """
     condition = condition.replace(" ", "")
+    if not condition:
+        # No condition to match!
+        return False
 
-    # compare equal
-    if condition[0] in "=" + string.digits:
-        if condition[0] == "=":
+    try:
+        # compare equal
+        if condition[0] in "=" + string.digits:
+            if condition[0] == "=":
+                compare_value = int(condition[1:])
+            else:
+                compare_value = int(condition)
+            return value == compare_value
+
+        # compare bigger
+        if condition[0] == ">":
             compare_value = int(condition[1:])
-        else:
-            compare_value = int(condition)
-        return value == compare_value
-
-    # compare bigger
-    if condition[0] == ">":
-        compare_value = int(condition[1:])
-        return value > compare_value
-
-    # compare less
-    if condition[0] == "<":
-        compare_value = int(condition[1:])
-        return value < compare_value
+            return value > compare_value
+
+        # compare less
+        if condition[0] == "<":
+            compare_value = int(condition[1:])
+            return value < compare_value
+    except ValueError:
+        log.warning(u"Invalid condition {0!s}. Needs to contain an integer.".format(condition))
+        return False
 
 
 def compare_value_value(value1, comparator, value2):


Test patch:
diff --git a/tests/test_lib_utils.py b/tests/test_lib_utils.py
--- a/tests/test_lib_utils.py
+++ b/tests/test_lib_utils.py
@@ -273,6 +273,12 @@ def test_08_compare_condition(self):
         self.assertFalse(compare_condition("<100", 1000))
         self.assertFalse(compare_condition("<100", 100))
 
+        # There are invalid conditions, which should not raise an exception
+        # An empty condition will result in False
+        self.assertFalse(compare_condition("", 100))
+        # An invalid condition, which misses a compare-value, will result in false
+        self.assertFalse(compare_condition(">", 100))
+
     def test_09_get_data_from_params(self):
         config_description = {
             "local":  {
@@ -643,6 +649,11 @@ def test_29_check_ip(self):
         self.assertTrue(excluded)
         self.assertTrue(found)
 
+        # run a test for empty condition
+        found, excluded = check_ip_in_policy("10.0.1.2", ["10.0.1.0/24", "!10.0.1.2", u'', None])
+        self.assertTrue(excluded)
+        self.assertTrue(found)
+
     def test_30_split_pin_pass(self):
         pin, otp = split_pin_pass("test1234", 4, True)
         self.assertEqual(pin, "test")