Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Ensure local invited & knocking users leave before purge. #16559

Merged
merged 2 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/16559.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a long-standing bug where invited/knocking users would not leave during a room purge.
7 changes: 4 additions & 3 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -1939,9 +1939,10 @@ async def shutdown_room(
else:
logger.info("Shutting down room %r", room_id)

users = await self.store.get_users_in_room(room_id)
for user_id in users:
if not self.hs.is_mine_id(user_id):
users = await self.store.get_local_users_related_to_room(room_id)
for user_id, membership in users:
# If the user is not in the room (or is banned), nothing to do.
if membership not in (Membership.JOIN, Membership.INVITE, Membership.KNOCK):
continue

logger.info("Kicking %r from %r...", user_id, room_id)
Expand Down
16 changes: 16 additions & 0 deletions synapse/storage/databases/main/roommember.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,22 @@ async def get_local_users_in_room(self, room_id: str) -> Sequence[str]:
desc="get_local_users_in_room",
)

async def get_local_users_related_to_room(
self, room_id: str
) -> List[Tuple[str, str]]:
"""
Retrieves a list of the current roommembers who are local to the server and their membership status.
"""
return cast(
List[Tuple[str, str]],
await self.db_pool.simple_select_list(
table="local_current_membership",
keyvalues={"room_id": room_id},
retcols=("user_id", "membership"),
desc="get_local_users_in_room",
),
)

async def check_local_user_in_room(self, user_id: str, room_id: str) -> bool:
"""
Check whether a given local user is currently joined to the given room.
Expand Down
53 changes: 52 additions & 1 deletion tests/rest/admin/test_room.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
PURGE_ROOM_ACTION_NAME,
SHUTDOWN_AND_PURGE_ROOM_ACTION_NAME,
)
from synapse.rest.client import directory, events, login, room
from synapse.rest.client import directory, events, knock, login, room, sync
from synapse.server import HomeServer
from synapse.types import UserID
from synapse.util import Clock
Expand All @@ -49,6 +49,8 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
login.register_servlets,
events.register_servlets,
room.register_servlets,
knock.register_servlets,
sync.register_servlets,
room.register_deprecated_servlets,
]

Expand Down Expand Up @@ -254,6 +256,55 @@ def test_purge_room_and_not_block(self) -> None:
self._is_blocked(self.room_id, expect=False)
self._has_no_members(self.room_id)

def test_purge_room_unjoined(self) -> None:
"""Test to purge a room when there are invited or knocked users."""
# Test that room is not purged
with self.assertRaises(AssertionError):
self._is_purged(self.room_id)

# Test that room is not blocked
self._is_blocked(self.room_id, expect=False)

# Assert one user in room
self._is_member(room_id=self.room_id, user_id=self.other_user)
self.helper.send_state(
self.room_id,
EventTypes.JoinRules,
{"join_rule": "knock"},
tok=self.other_user_tok,
)

# Invite a user.
invited_user = self.register_user("invited", "pass")
self.helper.invite(
self.room_id, self.other_user, invited_user, tok=self.other_user_tok
)

# Have a user knock.
knocked_user = self.register_user("knocked", "pass")
knocked_user_tok = self.login("knocked", "pass")
self.helper.knock(self.room_id, knocked_user, tok=knocked_user_tok)

channel = self.make_request(
"DELETE",
self.url.encode("ascii"),
content={"block": False, "purge": True},
access_token=self.admin_user_tok,
)

self.assertEqual(200, channel.code, msg=channel.json_body)
self.assertEqual(None, channel.json_body["new_room_id"])
self.assertCountEqual(
[self.other_user, invited_user, knocked_user],
channel.json_body["kicked_users"],
)
self.assertIn("failed_to_kick_users", channel.json_body)
self.assertIn("local_aliases", channel.json_body)

self._is_purged(self.room_id)
self._is_blocked(self.room_id, expect=False)
self._has_no_members(self.room_id)

def test_block_room_and_not_purge(self) -> None:
"""Test to block a room without purging it.
Members will not be moved to a new room and will not receive a message.
Expand Down