summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--changelog.d/17076.bugfix1
-rw-r--r--synapse/handlers/deactivate_account.py13
-rw-r--r--synapse/storage/databases/main/roommember.py22
-rw-r--r--tests/handlers/test_deactivate_account.py37
4 files changed, 72 insertions, 1 deletions
diff --git a/changelog.d/17076.bugfix b/changelog.d/17076.bugfix
new file mode 100644
index 0000000000..a111ea2b88
--- /dev/null
+++ b/changelog.d/17076.bugfix
@@ -0,0 +1 @@
+Redact membership events if the user requested erasure upon deactivating.
\ No newline at end of file
diff --git a/synapse/handlers/deactivate_account.py b/synapse/handlers/deactivate_account.py
index b13c4b6cb9..11ac377680 100644
--- a/synapse/handlers/deactivate_account.py
+++ b/synapse/handlers/deactivate_account.py
@@ -261,11 +261,22 @@ class DeactivateAccountHandler:
         user = UserID.from_string(user_id)
 
         rooms_for_user = await self.store.get_rooms_for_user(user_id)
+        requester = create_requester(user, authenticated_entity=self._server_name)
+        should_erase = await self.store.is_user_erased(user_id)
+
         for room_id in rooms_for_user:
             logger.info("User parter parting %r from %r", user_id, room_id)
             try:
+                # Before parting the user, redact all membership events if requested
+                if should_erase:
+                    event_ids = await self.store.get_membership_event_ids_for_user(
+                        user_id, room_id
+                    )
+                    for event_id in event_ids:
+                        await self.store.expire_event(event_id)
+
                 await self._room_member_handler.update_membership(
-                    create_requester(user, authenticated_entity=self._server_name),
+                    requester,
                     user,
                     room_id,
                     "leave",
diff --git a/synapse/storage/databases/main/roommember.py b/synapse/storage/databases/main/roommember.py
index 5d51502595..9fddbb2caf 100644
--- a/synapse/storage/databases/main/roommember.py
+++ b/synapse/storage/databases/main/roommember.py
@@ -1234,6 +1234,28 @@ class RoomMemberWorkerStore(EventsWorkerStore, CacheInvalidationWorkerStore):
 
         return set(room_ids)
 
+    async def get_membership_event_ids_for_user(
+        self, user_id: str, room_id: str
+    ) -> Set[str]:
+        """Get all event_ids for the given user and room.
+
+        Args:
+            user_id: The user ID to get the event IDs for.
+            room_id: The room ID to look up events for.
+
+        Returns:
+            Set of event IDs
+        """
+
+        event_ids = await self.db_pool.simple_select_onecol(
+            table="room_memberships",
+            keyvalues={"user_id": user_id, "room_id": room_id},
+            retcol="event_id",
+            desc="get_membership_event_ids_for_user",
+        )
+
+        return set(event_ids)
+
     @cached(max_entries=5000)
     async def _get_membership_from_event_id(
         self, member_event_id: str
diff --git a/tests/handlers/test_deactivate_account.py b/tests/handlers/test_deactivate_account.py
index b3f9e50f0f..c698771a06 100644
--- a/tests/handlers/test_deactivate_account.py
+++ b/tests/handlers/test_deactivate_account.py
@@ -424,3 +424,40 @@ class DeactivateAccountTestCase(HomeserverTestCase):
             self._store.get_knocked_at_rooms_for_local_user(self.user)
         )
         self.assertEqual(len(after_deactivate_knocks), 0)
+
+    def test_membership_is_redacted_upon_deactivation(self) -> None:
+        """
+        Tests that room membership events are redacted if erasure is requested.
+        """
+        # Create a room
+        room_id = self.helper.create_room_as(
+            self.user,
+            is_public=True,
+            tok=self.token,
+        )
+
+        # Change the displayname
+        membership_event, _ = self.get_success(
+            self.handler.update_membership(
+                requester=create_requester(self.user),
+                target=UserID.from_string(self.user),
+                room_id=room_id,
+                action=Membership.JOIN,
+                content={"displayname": "Hello World!"},
+            )
+        )
+
+        # Deactivate the account
+        self._deactivate_my_account()
+
+        # Get the all membership event IDs
+        membership_event_ids = self.get_success(
+            self._store.get_membership_event_ids_for_user(self.user, room_id=room_id)
+        )
+
+        # Get the events incl. JSON
+        events = self.get_success(self._store.get_events_as_list(membership_event_ids))
+
+        # Validate that there is no displayname in any of the events
+        for event in events:
+            self.assertTrue("displayname" not in event.content)