Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Stabilize support for MSC3873: disambuguated event push keys. #15190

Merged
merged 4 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/15190.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implement [MSC3873](https://github.com/matrix-org/matrix-spec-proposals/pull/3873) to fix a long-standing bug where properties with dots were handled ambiguously in push rules.
6 changes: 3 additions & 3 deletions rust/src/push/base_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub const BASE_APPEND_OVERRIDE_RULES: &[PushRule] = &[
priority_class: 5,
conditions: Cow::Borrowed(&[Condition::Known(KnownCondition::EventMatch(
EventMatchCondition {
key: Cow::Borrowed("content.m.relates_to.rel_type"),
key: Cow::Borrowed("content.m\\.relates_to.rel_type"),
pattern: Cow::Borrowed("m.replace"),
},
))]),
Expand Down Expand Up @@ -146,7 +146,7 @@ pub const BASE_APPEND_OVERRIDE_RULES: &[PushRule] = &[
priority_class: 5,
conditions: Cow::Borrowed(&[Condition::Known(
KnownCondition::ExactEventPropertyContainsType(EventPropertyIsTypeCondition {
key: Cow::Borrowed("content.org.matrix.msc3952.mentions.user_ids"),
key: Cow::Borrowed("content.org\\.matrix\\.msc3952\\.mentions.user_ids"),
value_type: Cow::Borrowed(&EventMatchPatternType::UserId),
}),
)]),
Expand All @@ -167,7 +167,7 @@ pub const BASE_APPEND_OVERRIDE_RULES: &[PushRule] = &[
priority_class: 5,
conditions: Cow::Borrowed(&[
Condition::Known(KnownCondition::EventPropertyIs(EventPropertyIsCondition {
key: Cow::Borrowed("content.org.matrix.msc3952.mentions.room"),
key: Cow::Borrowed("content.org\\.matrix\\.msc3952\\.mentions.room"),
value: Cow::Borrowed(&SimpleJsonValue::Bool(true)),
})),
Condition::Known(KnownCondition::SenderNotificationPermission {
Expand Down
10 changes: 0 additions & 10 deletions synapse/config/experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,6 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
# MSC3391: Removing account data.
self.msc3391_enabled = experimental.get("msc3391_enabled", False)

# MSC3873: Disambiguate event_match keys.
self.msc3873_escape_event_match_key = experimental.get(
"msc3873_escape_event_match_key", False
)

# MSC3952: Intentional mentions, this depends on MSC3966.
self.msc3952_intentional_mentions = experimental.get(
"msc3952_intentional_mentions", False
Expand All @@ -181,10 +176,5 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
"msc3958_supress_edit_notifs", False
)

# MSC3966: exact_event_property_contains push rule condition.
self.msc3966_exact_event_property_contains = experimental.get(
"msc3966_exact_event_property_contains", False
)

# MSC3967: Do not require UIA when first uploading cross signing keys
self.msc3967_enabled = experimental.get("msc3967_enabled", False)
33 changes: 8 additions & 25 deletions synapse/push/bulk_push_rule_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,10 +273,7 @@ async def _related_events(
related_event_id, allow_none=True
)
if related_event is not None:
related_events[relation_type] = _flatten_dict(
related_event,
msc3873_escape_event_match_key=self.hs.config.experimental.msc3873_escape_event_match_key,
)
related_events[relation_type] = _flatten_dict(related_event)

reply_event_id = (
event.content.get("m.relates_to", {})
Expand All @@ -291,10 +288,7 @@ async def _related_events(
)

if related_event is not None:
related_events["m.in_reply_to"] = _flatten_dict(
related_event,
msc3873_escape_event_match_key=self.hs.config.experimental.msc3873_escape_event_match_key,
)
related_events["m.in_reply_to"] = _flatten_dict(related_event)

# indicate that this is from a fallback relation.
if relation_type == "m.thread" and event.content.get(
Expand Down Expand Up @@ -401,10 +395,7 @@ async def _action_for_event_by_user(
)

evaluator = PushRuleEvaluator(
_flatten_dict(
event,
msc3873_escape_event_match_key=self.hs.config.experimental.msc3873_escape_event_match_key,
),
_flatten_dict(event),
has_mentions,
room_member_count,
sender_power_level,
Expand Down Expand Up @@ -494,8 +485,6 @@ def _flatten_dict(
d: Union[EventBase, Mapping[str, Any]],
prefix: Optional[List[str]] = None,
result: Optional[Dict[str, JsonValue]] = None,
*,
msc3873_escape_event_match_key: bool = False,
) -> Dict[str, JsonValue]:
"""
Given a JSON dictionary (or event) which might contain sub dictionaries,
Expand Down Expand Up @@ -524,24 +513,18 @@ def _flatten_dict(
if result is None:
result = {}
for key, value in d.items():
if msc3873_escape_event_match_key:
# Escape periods in the key with a backslash (and backslashes with an
# extra backslash). This is since a period is used as a separator between
# nested fields.
key = key.replace("\\", "\\\\").replace(".", "\\.")
# Escape periods in the key with a backslash (and backslashes with an
# extra backslash). This is since a period is used as a separator between
# nested fields.
key = key.replace("\\", "\\\\").replace(".", "\\.")

if _is_simple_value(value):
result[".".join(prefix + [key])] = value
elif isinstance(value, (list, tuple)):
result[".".join(prefix + [key])] = [v for v in value if _is_simple_value(v)]
elif isinstance(value, Mapping):
# do not set `room_version` due to recursion considerations below
_flatten_dict(
value,
prefix=(prefix + [key]),
result=result,
msc3873_escape_event_match_key=msc3873_escape_event_match_key,
)
_flatten_dict(value, prefix=(prefix + [key]), result=result)

# `room_version` should only ever be set when looking at the top level of an event
if (
Expand Down
10 changes: 3 additions & 7 deletions tests/push/test_push_rule_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,7 @@ def test_nested(self) -> None:

# If a field has a dot in it, escape it.
input = {"m.foo": {"b\\ar": "abc"}}
self.assertEqual({"m.foo.b\\ar": "abc"}, _flatten_dict(input))
self.assertEqual(
{"m\\.foo.b\\\\ar": "abc"},
_flatten_dict(input, msc3873_escape_event_match_key=True),
)
self.assertEqual({"m\\.foo.b\\\\ar": "abc"}, _flatten_dict(input))

def test_non_string(self) -> None:
"""String, booleans, ints, nulls and list of those should be kept while other items are dropped."""
Expand Down Expand Up @@ -125,7 +121,7 @@ def test_extensible_events(self) -> None:
"room_id": "!test:test",
"sender": "@alice:test",
"type": "m.room.message",
"content.org.matrix.msc1767.markup": [],
"content.org\\.matrix\\.msc1767\\.markup": [],
}
self.assertEqual(expected, _flatten_dict(event))

Expand All @@ -137,7 +133,7 @@ def test_extensible_events(self) -> None:
"room_id": "!test:test",
"sender": "@alice:test",
"type": "m.room.message",
"content.org.matrix.msc1767.markup": [],
"content.org\\.matrix\\.msc1767\\.markup": [],
}
self.assertEqual(expected, _flatten_dict(event))

Expand Down