Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redact membership events if the user requested erasure upon deactivating #17076

Merged
merged 11 commits into from
Apr 25, 2024
1 change: 1 addition & 0 deletions changelog.d/17076.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Redact membership events if the user requested erasure upon deactivating.
13 changes: 12 additions & 1 deletion synapse/handlers/deactivate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,22 @@ async def _part_user(self, user_id: str) -> None:
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",
Expand Down
22 changes: 22 additions & 0 deletions synapse/storage/databases/main/roommember.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,28 @@ async def get_rooms_user_has_been_in(self, user_id: str) -> Set[str]:

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
Expand Down
37 changes: 37 additions & 0 deletions tests/handlers/test_deactivate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,40 @@ def test_deactivate_account_rejects_knocks(self) -> None:
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)
Loading