summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard van der Hoff <1389908+richvdh@users.noreply.github.com>2022-02-28 17:40:24 +0000
committerGitHub <noreply@github.com>2022-02-28 17:40:24 +0000
commit6c0b44a3d73f73dc5913f081418347645dc84d6f (patch)
treec5f902578af302b30d2be221fea51c49731f5b32
parentActually fix bad debug logging rejecting device list & signing key transactio... (diff)
downloadsynapse-6c0b44a3d73f73dc5913f081418347645dc84d6f.tar.xz
Fix `PushRuleEvaluator` and `Filter` to work on frozendicts (#12100)
* Fix `PushRuleEvaluator` to work on frozendicts

frozendicts do not (necessarily) inherit from dict, so this needs to handle
them correctly.

* Fix event filtering for frozen events

Looks like this one was introduced by #11194.
-rw-r--r--changelog.d/12100.bugfix1
-rw-r--r--synapse/api/filtering.py5
-rw-r--r--synapse/push/push_rule_evaluator.py8
-rw-r--r--tests/api/test_filtering.py10
-rw-r--r--tests/push/test_push_rule_evaluator.py9
5 files changed, 27 insertions, 6 deletions
diff --git a/changelog.d/12100.bugfix b/changelog.d/12100.bugfix
new file mode 100644
index 0000000000..181095ad99
--- /dev/null
+++ b/changelog.d/12100.bugfix
@@ -0,0 +1 @@
+Fix a long-standing bug which could cause push notifications to malfunction if `use_frozen_dicts` was set in the configuration.
diff --git a/synapse/api/filtering.py b/synapse/api/filtering.py
index fe4cc2e8ee..cb532d7238 100644
--- a/synapse/api/filtering.py
+++ b/synapse/api/filtering.py
@@ -22,6 +22,7 @@ from typing import (
     Dict,
     Iterable,
     List,
+    Mapping,
     Optional,
     Set,
     TypeVar,
@@ -361,10 +362,10 @@ class Filter:
             return self._check_fields(field_matchers)
         else:
             content = event.get("content")
-            # Content is assumed to be a dict below, so ensure it is. This should
+            # Content is assumed to be a mapping below, so ensure it is. This should
             # always be true for events, but account_data has been allowed to
             # have non-dict content.
-            if not isinstance(content, dict):
+            if not isinstance(content, Mapping):
                 content = {}
 
             sender = event.get("sender", None)
diff --git a/synapse/push/push_rule_evaluator.py b/synapse/push/push_rule_evaluator.py
index 659a53805d..f617c759e6 100644
--- a/synapse/push/push_rule_evaluator.py
+++ b/synapse/push/push_rule_evaluator.py
@@ -15,12 +15,12 @@
 
 import logging
 import re
-from typing import Any, Dict, List, Optional, Pattern, Tuple, Union
+from typing import Any, Dict, List, Mapping, Optional, Pattern, Tuple, Union
 
 from matrix_common.regex import glob_to_regex, to_word_pattern
 
 from synapse.events import EventBase
-from synapse.types import JsonDict, UserID
+from synapse.types import UserID
 from synapse.util.caches.lrucache import LruCache
 
 logger = logging.getLogger(__name__)
@@ -223,7 +223,7 @@ def _glob_matches(glob: str, value: str, word_boundary: bool = False) -> bool:
 
 
 def _flatten_dict(
-    d: Union[EventBase, JsonDict],
+    d: Union[EventBase, Mapping[str, Any]],
     prefix: Optional[List[str]] = None,
     result: Optional[Dict[str, str]] = None,
 ) -> Dict[str, str]:
@@ -234,7 +234,7 @@ def _flatten_dict(
     for key, value in d.items():
         if isinstance(value, str):
             result[".".join(prefix + [key])] = value.lower()
-        elif isinstance(value, dict):
+        elif isinstance(value, Mapping):
             _flatten_dict(value, prefix=(prefix + [key]), result=result)
 
     return result
diff --git a/tests/api/test_filtering.py b/tests/api/test_filtering.py
index 2525018e95..8c3354ce3c 100644
--- a/tests/api/test_filtering.py
+++ b/tests/api/test_filtering.py
@@ -18,6 +18,7 @@
 from unittest.mock import patch
 
 import jsonschema
+from frozendict import frozendict
 
 from synapse.api.constants import EventContentFields
 from synapse.api.errors import SynapseError
@@ -327,6 +328,15 @@ class FilteringTestCase(unittest.HomeserverTestCase):
 
         self.assertFalse(Filter(self.hs, definition)._check(event))
 
+        # check it works with frozendicts too
+        event = MockEvent(
+            sender="@foo:bar",
+            type="m.room.message",
+            room_id="!secretbase:unknown",
+            content=frozendict({EventContentFields.LABELS: ["#fun"]}),
+        )
+        self.assertTrue(Filter(self.hs, definition)._check(event))
+
     def test_filter_not_labels(self):
         definition = {"org.matrix.not_labels": ["#fun"]}
         event = MockEvent(
diff --git a/tests/push/test_push_rule_evaluator.py b/tests/push/test_push_rule_evaluator.py
index a52e89e407..3849beb9d6 100644
--- a/tests/push/test_push_rule_evaluator.py
+++ b/tests/push/test_push_rule_evaluator.py
@@ -14,6 +14,8 @@
 
 from typing import Any, Dict
 
+import frozendict
+
 from synapse.api.room_versions import RoomVersions
 from synapse.events import FrozenEvent
 from synapse.push import push_rule_evaluator
@@ -191,6 +193,13 @@ class PushRuleEvaluatorTestCase(unittest.TestCase):
             "pattern should only match at the start/end of the value",
         )
 
+        # it should work on frozendicts too
+        self._assert_matches(
+            condition,
+            frozendict.frozendict({"value": "FoobaZ"}),
+            "patterns should match on frozendicts",
+        )
+
         # wildcards should match
         condition = {
             "kind": "event_match",