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

Commit

Permalink
Ensure local invited & knocking users leave before purge. (#16559)
Browse files Browse the repository at this point in the history
This is mostly useful for federated rooms where some users
would get stuck in the invite or knock state when the room
was purged from their homeserver.
  • Loading branch information
clokep authored Oct 27, 2023
1 parent 5413cef commit 2bf9341
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 4 deletions.
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

0 comments on commit 2bf9341

Please sign in to comment.