From b7d014102d5334ff9099cef9dd5af828a3f1dedf Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 10 May 2023 13:17:35 -0400 Subject: [PATCH 1/4] Implement MSC3821 to update redaction rules. --- synapse/api/room_versions.py | 39 ++++++++++++++++++++++++++++++ synapse/events/utils.py | 11 +++++++++ tests/events/test_utils.py | 47 +++++++++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/synapse/api/room_versions.py b/synapse/api/room_versions.py index 5d9c13e3c3fc..323f81d0c18b 100644 --- a/synapse/api/room_versions.py +++ b/synapse/api/room_versions.py @@ -101,6 +101,8 @@ class RoomVersion: msc3787_knock_restricted_join_rule: bool # MSC3667: Enforce integer power levels msc3667_int_only_power_levels: bool + # MSC3821: Do not redact the third_party_invite content field for membership events. + msc3821_redaction_rules: bool # MSC3931: Adds a push rule condition for "room version feature flags", making # some push rules room version dependent. Note that adding a flag to this list # is not enough to mark it "supported": the push rule evaluator also needs to @@ -130,6 +132,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -151,6 +154,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -172,6 +176,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -193,6 +198,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -214,6 +220,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -235,6 +242,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -256,6 +264,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -277,6 +286,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -298,6 +308,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -319,6 +330,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -340,6 +352,29 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=True, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, + msc3931_push_features=(), + msc3989_redaction_rules=False, + ) + MSC3821 = RoomVersion( + "org.matrix.msc3821.opt1", + RoomDisposition.UNSTABLE, + EventFormatVersions.ROOM_V4_PLUS, + StateResolutionVersions.V2, + enforce_key_validity=True, + special_case_aliases_auth=False, + strict_canonicaljson=True, + limit_notifications_power_levels=True, + msc2175_implicit_room_creator=False, + msc2176_redaction_rules=False, + msc3083_join_rules=True, + msc3375_redaction_rules=True, + msc2403_knocking=True, + msc2716_historical=False, + msc2716_redactions=False, + msc3787_knock_restricted_join_rule=False, + msc3667_int_only_power_levels=False, + msc3821_redaction_rules=True, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -361,6 +396,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=True, msc3667_int_only_power_levels=True, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -382,6 +418,7 @@ class RoomVersions: msc2716_redactions=True, msc3787_knock_restricted_join_rule=False, msc3667_int_only_power_levels=False, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=False, ) @@ -404,6 +441,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=True, msc3667_int_only_power_levels=True, + msc3821_redaction_rules=False, msc3931_push_features=(PushRuleRoomFlag.EXTENSIBLE_EVENTS,), msc3989_redaction_rules=False, ) @@ -425,6 +463,7 @@ class RoomVersions: msc2716_redactions=False, msc3787_knock_restricted_join_rule=True, msc3667_int_only_power_levels=True, + msc3821_redaction_rules=False, msc3931_push_features=(), msc3989_redaction_rules=True, ) diff --git a/synapse/events/utils.py b/synapse/events/utils.py index 0802eb1963e5..53af4d6aad11 100644 --- a/synapse/events/utils.py +++ b/synapse/events/utils.py @@ -130,6 +130,17 @@ def add_fields(*fields: str) -> None: add_fields("membership") if room_version.msc3375_redaction_rules: add_fields(EventContentFields.AUTHORISING_USER) + if room_version.msc3821_redaction_rules: + # Preserve the signed field under third_party_invite. + third_party_invite = event_dict["content"].get("third_party_invite") + if third_party_invite and isinstance( + third_party_invite, collections.abc.Mapping + ): + if "signed" in third_party_invite: + new_content["third_party_invite"] = { + "signed": third_party_invite["signed"] + } + elif event_type == EventTypes.Create: # MSC2176 rules state that create events cannot be redacted. if room_version.msc2176_redaction_rules: diff --git a/tests/events/test_utils.py b/tests/events/test_utils.py index 1b179acb20fc..d037c29715e0 100644 --- a/tests/events/test_utils.py +++ b/tests/events/test_utils.py @@ -392,7 +392,7 @@ def test_join_rules(self) -> None: ) def test_member(self) -> None: - """Member events have changed behavior starting with MSC3375.""" + """Member events have changed behavior in MSC3375 and MSC3821.""" self.run_test( { "type": "m.room.member", @@ -435,6 +435,51 @@ def test_member(self) -> None: room_version=RoomVersions.V9, ) + # After MSC3821, the signed key under third_party_invite is protected + # from redaction. + self.run_test( + { + "type": "m.room.member", + "content": { + "membership": "invite", + "third_party_invite": { + "signed": "foo", + "display_name": "stripped", + }, + "other_key": "stripped", + }, + }, + { + "type": "m.room.member", + "content": { + "membership": "invite", + "third_party_invite": {"signed": "foo"}, + }, + "signatures": {}, + "unsigned": {}, + }, + room_version=RoomVersions.MSC3821, + ) + + # Ensure this doesn't break if an invalid field is sent. + self.run_test( + { + "type": "m.room.member", + "content": { + "membership": "invite", + "third_party_invite": "stripped", + "other_key": "stripped", + }, + }, + { + "type": "m.room.member", + "content": {"membership": "invite"}, + "signatures": {}, + "unsigned": {}, + }, + room_version=RoomVersions.MSC3821, + ) + class SerializeEventTestCase(stdlib_unittest.TestCase): def serialize(self, ev: EventBase, fields: Optional[List[str]]) -> JsonDict: From 5e66a626bab2677b2c5fa010f756f7d52823a940 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 10 May 2023 13:21:58 -0400 Subject: [PATCH 2/4] Newsfragment --- changelog.d/15563.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/15563.misc diff --git a/changelog.d/15563.misc b/changelog.d/15563.misc new file mode 100644 index 000000000000..8bfecf2b9501 --- /dev/null +++ b/changelog.d/15563.misc @@ -0,0 +1 @@ +Implement [MSC3821](https://github.com/matrix-org/matrix-spec-proposals/pull/3821) to update the redaction rules. From d3154389fe64402e00e1e44daa7692a27494d03a Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 10 May 2023 14:13:04 -0400 Subject: [PATCH 3/4] Properly preserve an empty dict. --- synapse/events/utils.py | 11 +++++------ tests/events/test_utils.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/synapse/events/utils.py b/synapse/events/utils.py index 53af4d6aad11..5f6f8d0cba18 100644 --- a/synapse/events/utils.py +++ b/synapse/events/utils.py @@ -133,13 +133,12 @@ def add_fields(*fields: str) -> None: if room_version.msc3821_redaction_rules: # Preserve the signed field under third_party_invite. third_party_invite = event_dict["content"].get("third_party_invite") - if third_party_invite and isinstance( - third_party_invite, collections.abc.Mapping - ): + if isinstance(third_party_invite, collections.abc.Mapping): + new_content["third_party_invite"] = {} if "signed" in third_party_invite: - new_content["third_party_invite"] = { - "signed": third_party_invite["signed"] - } + new_content["third_party_invite"]["signed"] = third_party_invite[ + "signed" + ] elif event_type == EventTypes.Create: # MSC2176 rules state that create events cannot be redacted. diff --git a/tests/events/test_utils.py b/tests/events/test_utils.py index d037c29715e0..2b568de1d5b0 100644 --- a/tests/events/test_utils.py +++ b/tests/events/test_utils.py @@ -462,6 +462,24 @@ def test_member(self) -> None: ) # Ensure this doesn't break if an invalid field is sent. + self.run_test( + { + "type": "m.room.member", + "content": { + "membership": "invite", + "third_party_invite": {}, + "other_key": "stripped", + }, + }, + { + "type": "m.room.member", + "content": {"membership": "invite", "third_party_invite": {}}, + "signatures": {}, + "unsigned": {}, + }, + room_version=RoomVersions.MSC3821, + ) + self.run_test( { "type": "m.room.member", From 58f8783b5111a4a3fd95ea595cdf1b329bb78624 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 15 May 2023 08:57:46 -0400 Subject: [PATCH 4/4] Use more realistic data in tests. --- tests/events/test_utils.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/events/test_utils.py b/tests/events/test_utils.py index 2b568de1d5b0..c026d62e7412 100644 --- a/tests/events/test_utils.py +++ b/tests/events/test_utils.py @@ -437,15 +437,25 @@ def test_member(self) -> None: # After MSC3821, the signed key under third_party_invite is protected # from redaction. + THIRD_PARTY_INVITE = { + "display_name": "alice", + "signed": { + "mxid": "@alice:example.org", + "signatures": { + "magic.forest": { + "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } + }, + "token": "abc123", + }, + } + self.run_test( { "type": "m.room.member", "content": { "membership": "invite", - "third_party_invite": { - "signed": "foo", - "display_name": "stripped", - }, + "third_party_invite": THIRD_PARTY_INVITE, "other_key": "stripped", }, }, @@ -453,7 +463,7 @@ def test_member(self) -> None: "type": "m.room.member", "content": { "membership": "invite", - "third_party_invite": {"signed": "foo"}, + "third_party_invite": {"signed": THIRD_PARTY_INVITE["signed"]}, }, "signatures": {}, "unsigned": {},