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

Commit

Permalink
Stabilize support for MSC3873: disambuguated event push keys. (#15190)
Browse files Browse the repository at this point in the history
This removes the experimental configuration option and
always escapes the push rule condition keys.

Also escapes any (experimental) push rule condition keys
in the base rules which contain dot in a field name.
  • Loading branch information
clokep committed Mar 7, 2023
1 parent 47bc84d commit 20ed8c9
Show file tree
Hide file tree
Showing 5 changed files with 15 additions and 45 deletions.
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

0 comments on commit 20ed8c9

Please sign in to comment.