mirror of
https://mau.dev/maunium/synapse.git
synced 2024-12-13 23:23:44 +01:00
Implement MSC2174: move redacts to a content property. (#15395)
This moves `redacts` from being a top-level property to a `content` property in a new room version. MSC2176 (which was previously implemented) states to not `redact` this property.
This commit is contained in:
parent
c9723a1c1f
commit
2503126d52
8 changed files with 87 additions and 17 deletions
1
changelog.d/15395.misc
Normal file
1
changelog.d/15395.misc
Normal file
|
@ -0,0 +1 @@
|
|||
Implement [MSC2174](https://github.com/matrix-org/matrix-spec-proposals/pull/2174) to move the `redacts` key to a `content` property.
|
|
@ -80,7 +80,8 @@ class RoomVersion:
|
|||
limit_notifications_power_levels: bool
|
||||
# MSC2175: No longer include the creator in m.room.create events.
|
||||
msc2175_implicit_room_creator: bool
|
||||
# MSC2174/MSC2176: Apply updated redaction rules algorithm.
|
||||
# MSC2174/MSC2176: Apply updated redaction rules algorithm, move redacts to
|
||||
# content property.
|
||||
msc2176_redaction_rules: bool
|
||||
# MSC3083: Support the 'restricted' join_rule.
|
||||
msc3083_join_rules: bool
|
||||
|
|
|
@ -793,7 +793,7 @@ def check_redaction(
|
|||
"""Check whether the event sender is allowed to redact the target event.
|
||||
|
||||
Returns:
|
||||
True if the the sender is allowed to redact the target event if the
|
||||
True if the sender is allowed to redact the target event if the
|
||||
target event was created by them.
|
||||
False if the sender is allowed to redact the target event with no
|
||||
further checks.
|
||||
|
|
|
@ -326,7 +326,6 @@ class EventBase(metaclass=abc.ABCMeta):
|
|||
hashes: DictProperty[Dict[str, str]] = DictProperty("hashes")
|
||||
origin: DictProperty[str] = DictProperty("origin")
|
||||
origin_server_ts: DictProperty[int] = DictProperty("origin_server_ts")
|
||||
redacts: DefaultDictProperty[Optional[str]] = DefaultDictProperty("redacts", None)
|
||||
room_id: DictProperty[str] = DictProperty("room_id")
|
||||
sender: DictProperty[str] = DictProperty("sender")
|
||||
# TODO state_key should be Optional[str]. This is generally asserted in Synapse
|
||||
|
@ -346,6 +345,13 @@ class EventBase(metaclass=abc.ABCMeta):
|
|||
def membership(self) -> str:
|
||||
return self.content["membership"]
|
||||
|
||||
@property
|
||||
def redacts(self) -> Optional[str]:
|
||||
"""MSC2176 moved the redacts field into the content."""
|
||||
if self.room_version.msc2176_redaction_rules:
|
||||
return self.content.get("redacts")
|
||||
return self.get("redacts")
|
||||
|
||||
def is_state(self) -> bool:
|
||||
return self.get_state_key() is not None
|
||||
|
||||
|
|
|
@ -173,7 +173,9 @@ class EventBuilder:
|
|||
if self.is_state():
|
||||
event_dict["state_key"] = self._state_key
|
||||
|
||||
if self._redacts is not None:
|
||||
# MSC2174 moves the redacts property to the content, it is invalid to
|
||||
# provide it as a top-level property.
|
||||
if self._redacts is not None and not self.room_version.msc2176_redaction_rules:
|
||||
event_dict["redacts"] = self._redacts
|
||||
|
||||
if self._origin_server_ts is not None:
|
||||
|
|
|
@ -1096,6 +1096,7 @@ class RoomRedactEventRestServlet(TransactionRestServlet):
|
|||
super().__init__(hs)
|
||||
self.event_creation_handler = hs.get_event_creation_handler()
|
||||
self.auth = hs.get_auth()
|
||||
self._store = hs.get_datastores().main
|
||||
self._relation_handler = hs.get_relations_handler()
|
||||
self._msc3912_enabled = hs.config.experimental.msc3912_enabled
|
||||
|
||||
|
@ -1113,6 +1114,19 @@ class RoomRedactEventRestServlet(TransactionRestServlet):
|
|||
) -> Tuple[int, JsonDict]:
|
||||
content = parse_json_object_from_request(request)
|
||||
|
||||
# Ensure the redacts property in the content matches the one provided in
|
||||
# the URL.
|
||||
room_version = await self._store.get_room_version(room_id)
|
||||
if room_version.msc2176_redaction_rules:
|
||||
if "redacts" in content and content["redacts"] != event_id:
|
||||
raise SynapseError(
|
||||
400,
|
||||
"Cannot provide a redacts value incoherent with the event_id of the URL parameter",
|
||||
Codes.INVALID_PARAM,
|
||||
)
|
||||
else:
|
||||
content["redacts"] = event_id
|
||||
|
||||
try:
|
||||
with_relations = None
|
||||
if self._msc3912_enabled and "org.matrix.msc3912.with_relations" in content:
|
||||
|
@ -1128,20 +1142,23 @@ class RoomRedactEventRestServlet(TransactionRestServlet):
|
|||
requester, txn_id, room_id
|
||||
)
|
||||
|
||||
# Event is not yet redacted, create a new event to redact it.
|
||||
if event is None:
|
||||
event_dict = {
|
||||
"type": EventTypes.Redaction,
|
||||
"content": content,
|
||||
"room_id": room_id,
|
||||
"sender": requester.user.to_string(),
|
||||
}
|
||||
# Earlier room versions had a top-level redacts property.
|
||||
if not room_version.msc2176_redaction_rules:
|
||||
event_dict["redacts"] = event_id
|
||||
|
||||
(
|
||||
event,
|
||||
_,
|
||||
) = await self.event_creation_handler.create_and_send_nonmember_event(
|
||||
requester,
|
||||
{
|
||||
"type": EventTypes.Redaction,
|
||||
"content": content,
|
||||
"room_id": room_id,
|
||||
"sender": requester.user.to_string(),
|
||||
"redacts": event_id,
|
||||
},
|
||||
txn_id=txn_id,
|
||||
requester, event_dict, txn_id=txn_id
|
||||
)
|
||||
|
||||
if with_relations:
|
||||
|
|
|
@ -318,7 +318,11 @@ class PruneEventTestCase(stdlib_unittest.TestCase):
|
|||
"""Redaction events have no special behaviour until MSC2174/MSC2176."""
|
||||
|
||||
self.run_test(
|
||||
{"type": "m.room.redaction", "content": {"redacts": "$test2:domain"}},
|
||||
{
|
||||
"type": "m.room.redaction",
|
||||
"content": {"redacts": "$test2:domain"},
|
||||
"redacts": "$test2:domain",
|
||||
},
|
||||
{
|
||||
"type": "m.room.redaction",
|
||||
"content": {},
|
||||
|
@ -330,7 +334,11 @@ class PruneEventTestCase(stdlib_unittest.TestCase):
|
|||
|
||||
# After MSC2174, redaction events keep the redacts content key.
|
||||
self.run_test(
|
||||
{"type": "m.room.redaction", "content": {"redacts": "$test2:domain"}},
|
||||
{
|
||||
"type": "m.room.redaction",
|
||||
"content": {"redacts": "$test2:domain"},
|
||||
"redacts": "$test2:domain",
|
||||
},
|
||||
{
|
||||
"type": "m.room.redaction",
|
||||
"content": {"redacts": "$test2:domain"},
|
||||
|
|
|
@ -16,6 +16,7 @@ from typing import List, Optional
|
|||
from twisted.test.proto_helpers import MemoryReactor
|
||||
|
||||
from synapse.api.constants import EventTypes, RelationTypes
|
||||
from synapse.api.room_versions import RoomVersions
|
||||
from synapse.rest import admin
|
||||
from synapse.rest.client import login, room, sync
|
||||
from synapse.server import HomeServer
|
||||
|
@ -74,6 +75,7 @@ class RedactionsTestCase(HomeserverTestCase):
|
|||
event_id: str,
|
||||
expect_code: int = 200,
|
||||
with_relations: Optional[List[str]] = None,
|
||||
content: Optional[JsonDict] = None,
|
||||
) -> JsonDict:
|
||||
"""Helper function to send a redaction event.
|
||||
|
||||
|
@ -81,7 +83,7 @@ class RedactionsTestCase(HomeserverTestCase):
|
|||
"""
|
||||
path = "/_matrix/client/r0/rooms/%s/redact/%s" % (room_id, event_id)
|
||||
|
||||
request_content = {}
|
||||
request_content = content or {}
|
||||
if with_relations:
|
||||
request_content["org.matrix.msc3912.with_relations"] = with_relations
|
||||
|
||||
|
@ -92,7 +94,7 @@ class RedactionsTestCase(HomeserverTestCase):
|
|||
return channel.json_body
|
||||
|
||||
def _sync_room_timeline(self, access_token: str, room_id: str) -> List[JsonDict]:
|
||||
channel = self.make_request("GET", "sync", access_token=self.mod_access_token)
|
||||
channel = self.make_request("GET", "sync", access_token=access_token)
|
||||
self.assertEqual(channel.code, 200)
|
||||
room_sync = channel.json_body["rooms"]["join"][room_id]
|
||||
return room_sync["timeline"]["events"]
|
||||
|
@ -466,3 +468,36 @@ class RedactionsTestCase(HomeserverTestCase):
|
|||
)
|
||||
self.assertIn("body", event_dict["content"], event_dict)
|
||||
self.assertEqual("I'm in a thread!", event_dict["content"]["body"])
|
||||
|
||||
def test_content_redaction(self) -> None:
|
||||
"""MSC2174 moved the redacts property to the content."""
|
||||
# Create a room with the newer room version.
|
||||
room_id = self.helper.create_room_as(
|
||||
self.mod_user_id,
|
||||
tok=self.mod_access_token,
|
||||
room_version=RoomVersions.MSC2176.identifier,
|
||||
)
|
||||
|
||||
# Create an event.
|
||||
b = self.helper.send(room_id=room_id, tok=self.mod_access_token)
|
||||
event_id = b["event_id"]
|
||||
|
||||
# Attempt to redact it with a bogus event ID.
|
||||
self._redact_event(
|
||||
self.mod_access_token,
|
||||
room_id,
|
||||
event_id,
|
||||
expect_code=400,
|
||||
content={"redacts": "foo"},
|
||||
)
|
||||
|
||||
# Redact it for real.
|
||||
self._redact_event(self.mod_access_token, room_id, event_id)
|
||||
|
||||
# Sync the room, to get the id of the create event
|
||||
timeline = self._sync_room_timeline(self.mod_access_token, room_id)
|
||||
redact_event = timeline[-1]
|
||||
self.assertEqual(redact_event["type"], EventTypes.Redaction)
|
||||
# The redacts key should be in the content.
|
||||
self.assertNotIn("redacts", redact_event)
|
||||
self.assertEquals(redact_event["content"]["redacts"], event_id)
|
||||
|
|
Loading…
Reference in a new issue