diff --git a/changelog.d/15705.feature b/changelog.d/15705.feature
new file mode 100644
index 0000000000..e3cbb5a12e
--- /dev/null
+++ b/changelog.d/15705.feature
@@ -0,0 +1 @@
+Add a catch-all * to the supported relation types when redacting an event and its related events. This is an update to [MSC3912](https://github.com/matrix-org/matrix-spec-proposals/pull/3861) implementation.
diff --git a/synapse/handlers/relations.py b/synapse/handlers/relations.py
index 4824635162..db97f7aede 100644
--- a/synapse/handlers/relations.py
+++ b/synapse/handlers/relations.py
@@ -205,16 +205,22 @@ class RelationsHandler:
event_id: The event IDs to look and redact relations of.
initial_redaction_event: The redaction for the event referred to by
event_id.
- relation_types: The types of relations to look for.
+ relation_types: The types of relations to look for. If "*" is in the list,
+ all related events will be redacted regardless of the type.
Raises:
ShadowBanError if the requester is shadow-banned
"""
- related_event_ids = (
- await self._main_store.get_all_relations_for_event_with_types(
- event_id, relation_types
+ if "*" in relation_types:
+ related_event_ids = await self._main_store.get_all_relations_for_event(
+ event_id
+ )
+ else:
+ related_event_ids = (
+ await self._main_store.get_all_relations_for_event_with_types(
+ event_id, relation_types
+ )
)
- )
for related_event_id in related_event_ids:
try:
diff --git a/synapse/storage/databases/main/relations.py b/synapse/storage/databases/main/relations.py
index 4a6c6c724d..96908f14ba 100644
--- a/synapse/storage/databases/main/relations.py
+++ b/synapse/storage/databases/main/relations.py
@@ -365,6 +365,36 @@ class RelationsWorkerStore(SQLBaseStore):
func=get_all_relation_ids_for_event_with_types_txn,
)
+ async def get_all_relations_for_event(
+ self,
+ event_id: str,
+ ) -> List[str]:
+ """Get the event IDs of all events that have a relation to the given event.
+
+ Args:
+ event_id: The event for which to look for related events.
+
+ Returns:
+ A list of the IDs of the events that relate to the given event.
+ """
+
+ def get_all_relation_ids_for_event_txn(
+ txn: LoggingTransaction,
+ ) -> List[str]:
+ rows = self.db_pool.simple_select_list_txn(
+ txn=txn,
+ table="event_relations",
+ keyvalues={"relates_to_id": event_id},
+ retcols=["event_id"],
+ )
+
+ return [row["event_id"] for row in rows]
+
+ return await self.db_pool.runInteraction(
+ desc="get_all_relation_ids_for_event",
+ func=get_all_relation_ids_for_event_txn,
+ )
+
async def event_includes_relation(self, event_id: str) -> bool:
"""Check if the given event relates to another event.
diff --git a/tests/rest/client/test_redactions.py b/tests/rest/client/test_redactions.py
index 84a60c0b07..b43e95292c 100644
--- a/tests/rest/client/test_redactions.py
+++ b/tests/rest/client/test_redactions.py
@@ -217,9 +217,9 @@ class RedactionsTestCase(HomeserverTestCase):
self._redact_event(self.mod_access_token, self.room_id, msg_id)
@override_config({"experimental_features": {"msc3912_enabled": True}})
- def test_redact_relations(self) -> None:
- """Tests that we can redact the relations of an event at the same time as the
- event itself.
+ def test_redact_relations_with_types(self) -> None:
+ """Tests that we can redact the relations of an event of specific types
+ at the same time as the event itself.
"""
# Send a root event.
res = self.helper.send_event(
@@ -318,6 +318,104 @@ class RedactionsTestCase(HomeserverTestCase):
self.assertNotIn("redacted_because", event_dict, event_dict)
@override_config({"experimental_features": {"msc3912_enabled": True}})
+ def test_redact_all_relations(self) -> None:
+ """Tests that we can redact all the relations of an event at the same time as the
+ event itself.
+ """
+ # Send a root event.
+ res = self.helper.send_event(
+ room_id=self.room_id,
+ type=EventTypes.Message,
+ content={"msgtype": "m.text", "body": "hello"},
+ tok=self.mod_access_token,
+ )
+ root_event_id = res["event_id"]
+
+ # Send an edit to this root event.
+ res = self.helper.send_event(
+ room_id=self.room_id,
+ type=EventTypes.Message,
+ content={
+ "body": " * hello world",
+ "m.new_content": {
+ "body": "hello world",
+ "msgtype": "m.text",
+ },
+ "m.relates_to": {
+ "event_id": root_event_id,
+ "rel_type": RelationTypes.REPLACE,
+ },
+ "msgtype": "m.text",
+ },
+ tok=self.mod_access_token,
+ )
+ edit_event_id = res["event_id"]
+
+ # Also send a threaded message whose root is the same as the edit's.
+ res = self.helper.send_event(
+ room_id=self.room_id,
+ type=EventTypes.Message,
+ content={
+ "msgtype": "m.text",
+ "body": "message 1",
+ "m.relates_to": {
+ "event_id": root_event_id,
+ "rel_type": RelationTypes.THREAD,
+ },
+ },
+ tok=self.mod_access_token,
+ )
+ threaded_event_id = res["event_id"]
+
+ # Also send a reaction, again with the same root.
+ res = self.helper.send_event(
+ room_id=self.room_id,
+ type=EventTypes.Reaction,
+ content={
+ "m.relates_to": {
+ "rel_type": RelationTypes.ANNOTATION,
+ "event_id": root_event_id,
+ "key": "👍",
+ }
+ },
+ tok=self.mod_access_token,
+ )
+ reaction_event_id = res["event_id"]
+
+ # Redact the root event, specifying that we also want to delete all events that
+ # relate to it.
+ self._redact_event(
+ self.mod_access_token,
+ self.room_id,
+ root_event_id,
+ with_relations=["*"],
+ )
+
+ # Check that the root event got redacted.
+ event_dict = self.helper.get_event(
+ self.room_id, root_event_id, self.mod_access_token
+ )
+ self.assertIn("redacted_because", event_dict, event_dict)
+
+ # Check that the edit got redacted.
+ event_dict = self.helper.get_event(
+ self.room_id, edit_event_id, self.mod_access_token
+ )
+ self.assertIn("redacted_because", event_dict, event_dict)
+
+ # Check that the threaded message got redacted.
+ event_dict = self.helper.get_event(
+ self.room_id, threaded_event_id, self.mod_access_token
+ )
+ self.assertIn("redacted_because", event_dict, event_dict)
+
+ # Check that the reaction got redacted.
+ event_dict = self.helper.get_event(
+ self.room_id, reaction_event_id, self.mod_access_token
+ )
+ self.assertIn("redacted_because", event_dict, event_dict)
+
+ @override_config({"experimental_features": {"msc3912_enabled": True}})
def test_redact_relations_no_perms(self) -> None:
"""Tests that, when redacting a message along with its relations, if not all
the related messages can be redacted because of insufficient permissions, the
|