Ensure local invited & knocking users leave before purge. (#16559)
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.pull/16572/head
parent
5413cefe32
commit
2bf9341406
|
@ -0,0 +1 @@
|
||||||
|
Fix a long-standing bug where invited/knocking users would not leave during a room purge.
|
|
@ -1939,9 +1939,10 @@ class RoomShutdownHandler:
|
||||||
else:
|
else:
|
||||||
logger.info("Shutting down room %r", room_id)
|
logger.info("Shutting down room %r", room_id)
|
||||||
|
|
||||||
users = await self.store.get_users_in_room(room_id)
|
users = await self.store.get_local_users_related_to_room(room_id)
|
||||||
for user_id in users:
|
for user_id, membership in users:
|
||||||
if not self.hs.is_mine_id(user_id):
|
# 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
|
continue
|
||||||
|
|
||||||
logger.info("Kicking %r from %r...", user_id, room_id)
|
logger.info("Kicking %r from %r...", user_id, room_id)
|
||||||
|
|
|
@ -482,6 +482,22 @@ class RoomMemberWorkerStore(EventsWorkerStore, CacheInvalidationWorkerStore):
|
||||||
desc="get_local_users_in_room",
|
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:
|
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.
|
Check whether a given local user is currently joined to the given room.
|
||||||
|
|
|
@ -29,7 +29,7 @@ from synapse.handlers.pagination import (
|
||||||
PURGE_ROOM_ACTION_NAME,
|
PURGE_ROOM_ACTION_NAME,
|
||||||
SHUTDOWN_AND_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.server import HomeServer
|
||||||
from synapse.types import UserID
|
from synapse.types import UserID
|
||||||
from synapse.util import Clock
|
from synapse.util import Clock
|
||||||
|
@ -49,6 +49,8 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
|
||||||
login.register_servlets,
|
login.register_servlets,
|
||||||
events.register_servlets,
|
events.register_servlets,
|
||||||
room.register_servlets,
|
room.register_servlets,
|
||||||
|
knock.register_servlets,
|
||||||
|
sync.register_servlets,
|
||||||
room.register_deprecated_servlets,
|
room.register_deprecated_servlets,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -254,6 +256,55 @@ class DeleteRoomTestCase(unittest.HomeserverTestCase):
|
||||||
self._is_blocked(self.room_id, expect=False)
|
self._is_blocked(self.room_id, expect=False)
|
||||||
self._has_no_members(self.room_id)
|
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:
|
def test_block_room_and_not_purge(self) -> None:
|
||||||
"""Test to block a room without purging it.
|
"""Test to block a room without purging it.
|
||||||
Members will not be moved to a new room and will not receive a message.
|
Members will not be moved to a new room and will not receive a message.
|
||||||
|
|
Loading…
Reference in New Issue