diff --git a/synapse/handlers/user_directory.py b/synapse/handlers/user_directory.py
index 22905eda12..de7ecd42b8 100644
--- a/synapse/handlers/user_directory.py
+++ b/synapse/handlers/user_directory.py
@@ -413,11 +413,13 @@ class UserDirectoryHandler(StateDeltasHandler):
# Remove user from sharing tables
await self.store.remove_user_who_share_room(user_id, room_id)
- # Are they still in any rooms? If not, remove them entirely.
- rooms_user_is_in = await self.store.get_user_dir_rooms_user_is_in(user_id)
+ # If they're a remote user and not in any rooms we can see,
+ # remove their user_directory entry.
+ if not self.is_mine_id(user_id):
+ rooms_user_is_in = await self.store.get_user_dir_rooms_user_is_in(user_id)
- if len(rooms_user_is_in) == 0:
- await self.store.remove_from_user_dir(user_id)
+ if len(rooms_user_is_in) == 0:
+ await self.store.remove_from_user_dir(user_id)
async def _handle_possible_remote_profile_change(
self,
diff --git a/tests/handlers/test_user_directory.py b/tests/handlers/test_user_directory.py
index db65253773..07d9df47d0 100644
--- a/tests/handlers/test_user_directory.py
+++ b/tests/handlers/test_user_directory.py
@@ -503,6 +503,35 @@ class UserDirectoryTestCase(unittest.HomeserverTestCase):
0,
)
+ def test_local_user_remains_in_directory_after_leaving_all_rooms(self) -> None:
+ """We should preserve the invariant that local, non-excluded users are
+ always in the user_directory table.
+
+ This is a choice to simplify the implementation, and also ensure that
+ the config option to search for all users works in this case."""
+ alice = self.register_user("alice", "pass")
+ alice_token = self.login(alice, "pass")
+
+ # Alice should have a user directory entry created at registration.
+ users = self.get_success(self.user_dir_helper.get_profiles_in_user_directory())
+ self.assertEqual(
+ users, {alice: ProfileInfo(display_name="alice", avatar_url=None)}
+ )
+
+ # Alice makes a room for herself.
+ room = self.helper.create_room_as(alice, tok=alice_token)
+
+ # Alice leaves that room.
+ self.helper.leave(room, alice, tok=alice_token)
+
+ # Wait for background updates to ensure that the user directory handler
+ # handler has processed all events. Alice should remain in the directory.
+ self.wait_for_background_updates()
+ users = self.get_success(self.user_dir_helper.get_profiles_in_user_directory())
+ self.assertEqual(
+ users, {alice: ProfileInfo(display_name="alice", avatar_url=None)}
+ )
+
def test_private_room(self) -> None:
"""
A user can be searched for only by people that are either in a public
|