You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

743 line
29 KiB

  1. # Copyright 2018 New Vector
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from typing import Tuple
  15. from unittest.mock import Mock, patch
  16. from urllib.parse import quote
  17. from twisted.internet import defer
  18. from twisted.test.proto_helpers import MemoryReactor
  19. import synapse.rest.admin
  20. from synapse.api.constants import UserTypes
  21. from synapse.api.room_versions import RoomVersion, RoomVersions
  22. from synapse.appservice import ApplicationService
  23. from synapse.rest.client import login, register, room, user_directory
  24. from synapse.server import HomeServer
  25. from synapse.storage.roommember import ProfileInfo
  26. from synapse.types import create_requester
  27. from synapse.util import Clock
  28. from tests import unittest
  29. from tests.storage.test_user_directory import GetUserDirectoryTables
  30. from tests.test_utils.event_injection import inject_member_event
  31. from tests.unittest import override_config
  32. class UserDirectoryTestCase(unittest.HomeserverTestCase):
  33. """Tests the UserDirectoryHandler.
  34. We're broadly testing two kinds of things here.
  35. 1. Check that we correctly update the user directory in response
  36. to events (e.g. join a room, leave a room, change name, make public)
  37. 2. Check that the search logic behaves as expected.
  38. The background process that rebuilds the user directory is tested in
  39. tests/storage/test_user_directory.py.
  40. """
  41. servlets = [
  42. login.register_servlets,
  43. synapse.rest.admin.register_servlets,
  44. register.register_servlets,
  45. room.register_servlets,
  46. ]
  47. def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
  48. config = self.default_config()
  49. config["update_user_directory"] = True
  50. self.appservice = ApplicationService(
  51. token="i_am_an_app_service",
  52. hostname="test",
  53. id="1234",
  54. namespaces={"users": [{"regex": r"@as_user.*", "exclusive": True}]},
  55. sender="@as:test",
  56. )
  57. mock_load_appservices = Mock(return_value=[self.appservice])
  58. with patch(
  59. "synapse.storage.databases.main.appservice.load_appservices",
  60. mock_load_appservices,
  61. ):
  62. hs = self.setup_test_homeserver(config=config)
  63. return hs
  64. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  65. self.store = hs.get_datastore()
  66. self.handler = hs.get_user_directory_handler()
  67. self.event_builder_factory = self.hs.get_event_builder_factory()
  68. self.event_creation_handler = self.hs.get_event_creation_handler()
  69. self.user_dir_helper = GetUserDirectoryTables(self.store)
  70. def test_normal_user_pair(self) -> None:
  71. """Sanity check that the room-sharing tables are updated correctly."""
  72. alice = self.register_user("alice", "pass")
  73. alice_token = self.login(alice, "pass")
  74. bob = self.register_user("bob", "pass")
  75. bob_token = self.login(bob, "pass")
  76. public = self.helper.create_room_as(
  77. alice,
  78. is_public=True,
  79. extra_content={"visibility": "public"},
  80. tok=alice_token,
  81. )
  82. private = self.helper.create_room_as(alice, is_public=False, tok=alice_token)
  83. self.helper.invite(private, alice, bob, tok=alice_token)
  84. self.helper.join(public, bob, tok=bob_token)
  85. self.helper.join(private, bob, tok=bob_token)
  86. # Alice also makes a second public room but no-one else joins
  87. public2 = self.helper.create_room_as(
  88. alice,
  89. is_public=True,
  90. extra_content={"visibility": "public"},
  91. tok=alice_token,
  92. )
  93. users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
  94. in_public = self.get_success(self.user_dir_helper.get_users_in_public_rooms())
  95. in_private = self.get_success(
  96. self.user_dir_helper.get_users_who_share_private_rooms()
  97. )
  98. self.assertEqual(users, {alice, bob})
  99. self.assertEqual(
  100. set(in_public), {(alice, public), (bob, public), (alice, public2)}
  101. )
  102. self.assertEqual(
  103. self.user_dir_helper._compress_shared(in_private),
  104. {(alice, bob, private), (bob, alice, private)},
  105. )
  106. # The next three tests (test_population_excludes_*) all setup
  107. # - A normal user included in the user dir
  108. # - A public and private room created by that user
  109. # - A user excluded from the room dir, belonging to both rooms
  110. # They match similar logic in storage/test_user_directory. But that tests
  111. # rebuilding the directory; this tests updating it incrementally.
  112. def test_excludes_support_user(self) -> None:
  113. alice = self.register_user("alice", "pass")
  114. alice_token = self.login(alice, "pass")
  115. support = "@support1:test"
  116. self.get_success(
  117. self.store.register_user(
  118. user_id=support, password_hash=None, user_type=UserTypes.SUPPORT
  119. )
  120. )
  121. public, private = self._create_rooms_and_inject_memberships(
  122. alice, alice_token, support
  123. )
  124. self._check_only_one_user_in_directory(alice, public)
  125. def test_excludes_deactivated_user(self) -> None:
  126. admin = self.register_user("admin", "pass", admin=True)
  127. admin_token = self.login(admin, "pass")
  128. user = self.register_user("naughty", "pass")
  129. # Deactivate the user.
  130. channel = self.make_request(
  131. "PUT",
  132. f"/_synapse/admin/v2/users/{user}",
  133. access_token=admin_token,
  134. content={"deactivated": True},
  135. )
  136. self.assertEqual(channel.code, 200)
  137. self.assertEqual(channel.json_body["deactivated"], True)
  138. # Join the deactivated user to rooms owned by the admin.
  139. # Is this something that could actually happen outside of a test?
  140. public, private = self._create_rooms_and_inject_memberships(
  141. admin, admin_token, user
  142. )
  143. self._check_only_one_user_in_directory(admin, public)
  144. def test_excludes_appservices_user(self) -> None:
  145. # Register an AS user.
  146. user = self.register_user("user", "pass")
  147. token = self.login(user, "pass")
  148. as_user = self.register_appservice_user("as_user_potato", self.appservice.token)
  149. # Join the AS user to rooms owned by the normal user.
  150. public, private = self._create_rooms_and_inject_memberships(
  151. user, token, as_user
  152. )
  153. self._check_only_one_user_in_directory(user, public)
  154. def _create_rooms_and_inject_memberships(
  155. self, creator: str, token: str, joiner: str
  156. ) -> Tuple[str, str]:
  157. """Create a public and private room as a normal user.
  158. Then get the `joiner` into those rooms.
  159. """
  160. # TODO: Duplicates the same-named method in UserDirectoryInitialPopulationTest.
  161. public_room = self.helper.create_room_as(
  162. creator,
  163. is_public=True,
  164. # See https://github.com/matrix-org/synapse/issues/10951
  165. extra_content={"visibility": "public"},
  166. tok=token,
  167. )
  168. private_room = self.helper.create_room_as(creator, is_public=False, tok=token)
  169. # HACK: get the user into these rooms
  170. self.get_success(inject_member_event(self.hs, public_room, joiner, "join"))
  171. self.get_success(inject_member_event(self.hs, private_room, joiner, "join"))
  172. return public_room, private_room
  173. def _check_only_one_user_in_directory(self, user: str, public: str) -> None:
  174. users = self.get_success(self.user_dir_helper.get_users_in_user_directory())
  175. in_public = self.get_success(self.user_dir_helper.get_users_in_public_rooms())
  176. in_private = self.get_success(
  177. self.user_dir_helper.get_users_who_share_private_rooms()
  178. )
  179. self.assertEqual(users, {user})
  180. self.assertEqual(set(in_public), {(user, public)})
  181. self.assertEqual(in_private, [])
  182. def test_handle_local_profile_change_with_support_user(self) -> None:
  183. support_user_id = "@support:test"
  184. self.get_success(
  185. self.store.register_user(
  186. user_id=support_user_id, password_hash=None, user_type=UserTypes.SUPPORT
  187. )
  188. )
  189. regular_user_id = "@regular:test"
  190. self.get_success(
  191. self.store.register_user(user_id=regular_user_id, password_hash=None)
  192. )
  193. self.get_success(
  194. self.handler.handle_local_profile_change(
  195. support_user_id, ProfileInfo("I love support me", None)
  196. )
  197. )
  198. profile = self.get_success(self.store.get_user_in_directory(support_user_id))
  199. self.assertTrue(profile is None)
  200. display_name = "display_name"
  201. profile_info = ProfileInfo(avatar_url="avatar_url", display_name=display_name)
  202. self.get_success(
  203. self.handler.handle_local_profile_change(regular_user_id, profile_info)
  204. )
  205. profile = self.get_success(self.store.get_user_in_directory(regular_user_id))
  206. self.assertTrue(profile["display_name"] == display_name)
  207. def test_handle_local_profile_change_with_deactivated_user(self) -> None:
  208. # create user
  209. r_user_id = "@regular:test"
  210. self.get_success(
  211. self.store.register_user(user_id=r_user_id, password_hash=None)
  212. )
  213. # update profile
  214. display_name = "Regular User"
  215. profile_info = ProfileInfo(avatar_url="avatar_url", display_name=display_name)
  216. self.get_success(
  217. self.handler.handle_local_profile_change(r_user_id, profile_info)
  218. )
  219. # profile is in directory
  220. profile = self.get_success(self.store.get_user_in_directory(r_user_id))
  221. self.assertTrue(profile["display_name"] == display_name)
  222. # deactivate user
  223. self.get_success(self.store.set_user_deactivated_status(r_user_id, True))
  224. self.get_success(self.handler.handle_local_user_deactivated(r_user_id))
  225. # profile is not in directory
  226. profile = self.get_success(self.store.get_user_in_directory(r_user_id))
  227. self.assertTrue(profile is None)
  228. # update profile after deactivation
  229. self.get_success(
  230. self.handler.handle_local_profile_change(r_user_id, profile_info)
  231. )
  232. # profile is furthermore not in directory
  233. profile = self.get_success(self.store.get_user_in_directory(r_user_id))
  234. self.assertTrue(profile is None)
  235. def test_handle_local_profile_change_with_appservice_user(self) -> None:
  236. # create user
  237. as_user_id = self.register_appservice_user(
  238. "as_user_alice", self.appservice.token
  239. )
  240. # profile is not in directory
  241. profile = self.get_success(self.store.get_user_in_directory(as_user_id))
  242. self.assertTrue(profile is None)
  243. # update profile
  244. profile_info = ProfileInfo(avatar_url="avatar_url", display_name="4L1c3")
  245. self.get_success(
  246. self.handler.handle_local_profile_change(as_user_id, profile_info)
  247. )
  248. # profile is still not in directory
  249. profile = self.get_success(self.store.get_user_in_directory(as_user_id))
  250. self.assertTrue(profile is None)
  251. def test_handle_user_deactivated_support_user(self) -> None:
  252. s_user_id = "@support:test"
  253. self.get_success(
  254. self.store.register_user(
  255. user_id=s_user_id, password_hash=None, user_type=UserTypes.SUPPORT
  256. )
  257. )
  258. mock_remove_from_user_dir = Mock(return_value=defer.succeed(None))
  259. with patch.object(
  260. self.store, "remove_from_user_dir", mock_remove_from_user_dir
  261. ):
  262. self.get_success(self.handler.handle_local_user_deactivated(s_user_id))
  263. # BUG: the correct spelling is assert_not_called, but that makes the test fail
  264. # and it's not clear that this is actually the behaviour we want.
  265. mock_remove_from_user_dir.not_called()
  266. def test_handle_user_deactivated_regular_user(self) -> None:
  267. r_user_id = "@regular:test"
  268. self.get_success(
  269. self.store.register_user(user_id=r_user_id, password_hash=None)
  270. )
  271. mock_remove_from_user_dir = Mock(return_value=defer.succeed(None))
  272. with patch.object(
  273. self.store, "remove_from_user_dir", mock_remove_from_user_dir
  274. ):
  275. self.get_success(self.handler.handle_local_user_deactivated(r_user_id))
  276. mock_remove_from_user_dir.assert_called_once_with(r_user_id)
  277. def test_reactivation_makes_regular_user_searchable(self) -> None:
  278. user = self.register_user("regular", "pass")
  279. user_token = self.login(user, "pass")
  280. admin_user = self.register_user("admin", "pass", admin=True)
  281. admin_token = self.login(admin_user, "pass")
  282. # Ensure the regular user is publicly visible and searchable.
  283. self.helper.create_room_as(user, is_public=True, tok=user_token)
  284. s = self.get_success(self.handler.search_users(admin_user, user, 10))
  285. self.assertEqual(len(s["results"]), 1)
  286. self.assertEqual(s["results"][0]["user_id"], user)
  287. # Deactivate the user and check they're not searchable.
  288. deactivate_handler = self.hs.get_deactivate_account_handler()
  289. self.get_success(
  290. deactivate_handler.deactivate_account(
  291. user, erase_data=False, requester=create_requester(admin_user)
  292. )
  293. )
  294. s = self.get_success(self.handler.search_users(admin_user, user, 10))
  295. self.assertEqual(s["results"], [])
  296. # Reactivate the user
  297. channel = self.make_request(
  298. "PUT",
  299. f"/_synapse/admin/v2/users/{quote(user)}",
  300. access_token=admin_token,
  301. content={"deactivated": False, "password": "pass"},
  302. )
  303. self.assertEqual(channel.code, 200)
  304. user_token = self.login(user, "pass")
  305. self.helper.create_room_as(user, is_public=True, tok=user_token)
  306. # Check they're searchable.
  307. s = self.get_success(self.handler.search_users(admin_user, user, 10))
  308. self.assertEqual(len(s["results"]), 1)
  309. self.assertEqual(s["results"][0]["user_id"], user)
  310. def test_process_join_after_server_leaves_room(self) -> None:
  311. alice = self.register_user("alice", "pass")
  312. alice_token = self.login(alice, "pass")
  313. bob = self.register_user("bob", "pass")
  314. bob_token = self.login(bob, "pass")
  315. # Alice makes two rooms. Bob joins one of them.
  316. room1 = self.helper.create_room_as(alice, tok=alice_token)
  317. room2 = self.helper.create_room_as(alice, tok=alice_token)
  318. print("room1=", room1)
  319. print("room2=", room2)
  320. self.helper.join(room1, bob, tok=bob_token)
  321. # The user sharing tables should have been updated.
  322. public1 = self.get_success(self.user_dir_helper.get_users_in_public_rooms())
  323. self.assertEqual(set(public1), {(alice, room1), (alice, room2), (bob, room1)})
  324. # Alice leaves room1. The user sharing tables should be updated.
  325. self.helper.leave(room1, alice, tok=alice_token)
  326. public2 = self.get_success(self.user_dir_helper.get_users_in_public_rooms())
  327. self.assertEqual(set(public2), {(alice, room2), (bob, room1)})
  328. # Pause the processing of new events.
  329. dir_handler = self.hs.get_user_directory_handler()
  330. dir_handler.update_user_directory = False
  331. # Bob leaves one room and joins the other.
  332. self.helper.leave(room1, bob, tok=bob_token)
  333. self.helper.join(room2, bob, tok=bob_token)
  334. # Process the leave and join in one go.
  335. dir_handler.update_user_directory = True
  336. dir_handler.notify_new_event()
  337. self.wait_for_background_updates()
  338. # The user sharing tables should have been updated.
  339. public3 = self.get_success(self.user_dir_helper.get_users_in_public_rooms())
  340. self.assertEqual(set(public3), {(alice, room2), (bob, room2)})
  341. def test_private_room(self) -> None:
  342. """
  343. A user can be searched for only by people that are either in a public
  344. room, or that share a private chat.
  345. """
  346. u1 = self.register_user("user1", "pass")
  347. u1_token = self.login(u1, "pass")
  348. u2 = self.register_user("user2", "pass")
  349. u2_token = self.login(u2, "pass")
  350. u3 = self.register_user("user3", "pass")
  351. # We do not add users to the directory until they join a room.
  352. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  353. self.assertEqual(len(s["results"]), 0)
  354. room = self.helper.create_room_as(u1, is_public=False, tok=u1_token)
  355. self.helper.invite(room, src=u1, targ=u2, tok=u1_token)
  356. self.helper.join(room, user=u2, tok=u2_token)
  357. # Check we have populated the database correctly.
  358. shares_private = self.get_success(
  359. self.user_dir_helper.get_users_who_share_private_rooms()
  360. )
  361. public_users = self.get_success(
  362. self.user_dir_helper.get_users_in_public_rooms()
  363. )
  364. self.assertEqual(
  365. self.user_dir_helper._compress_shared(shares_private),
  366. {(u1, u2, room), (u2, u1, room)},
  367. )
  368. self.assertEqual(public_users, [])
  369. # We get one search result when searching for user2 by user1.
  370. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  371. self.assertEqual(len(s["results"]), 1)
  372. # We get NO search results when searching for user2 by user3.
  373. s = self.get_success(self.handler.search_users(u3, "user2", 10))
  374. self.assertEqual(len(s["results"]), 0)
  375. # We get NO search results when searching for user3 by user1.
  376. s = self.get_success(self.handler.search_users(u1, "user3", 10))
  377. self.assertEqual(len(s["results"]), 0)
  378. # User 2 then leaves.
  379. self.helper.leave(room, user=u2, tok=u2_token)
  380. # Check we have removed the values.
  381. shares_private = self.get_success(
  382. self.user_dir_helper.get_users_who_share_private_rooms()
  383. )
  384. public_users = self.get_success(
  385. self.user_dir_helper.get_users_in_public_rooms()
  386. )
  387. self.assertEqual(self.user_dir_helper._compress_shared(shares_private), set())
  388. self.assertEqual(public_users, [])
  389. # User1 now gets no search results for any of the other users.
  390. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  391. self.assertEqual(len(s["results"]), 0)
  392. s = self.get_success(self.handler.search_users(u1, "user3", 10))
  393. self.assertEqual(len(s["results"]), 0)
  394. def test_spam_checker(self) -> None:
  395. """
  396. A user which fails the spam checks will not appear in search results.
  397. """
  398. u1 = self.register_user("user1", "pass")
  399. u1_token = self.login(u1, "pass")
  400. u2 = self.register_user("user2", "pass")
  401. u2_token = self.login(u2, "pass")
  402. # We do not add users to the directory until they join a room.
  403. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  404. self.assertEqual(len(s["results"]), 0)
  405. room = self.helper.create_room_as(u1, is_public=False, tok=u1_token)
  406. self.helper.invite(room, src=u1, targ=u2, tok=u1_token)
  407. self.helper.join(room, user=u2, tok=u2_token)
  408. # Check we have populated the database correctly.
  409. shares_private = self.get_success(
  410. self.user_dir_helper.get_users_who_share_private_rooms()
  411. )
  412. public_users = self.get_success(
  413. self.user_dir_helper.get_users_in_public_rooms()
  414. )
  415. self.assertEqual(
  416. self.user_dir_helper._compress_shared(shares_private),
  417. {(u1, u2, room), (u2, u1, room)},
  418. )
  419. self.assertEqual(public_users, [])
  420. # We get one search result when searching for user2 by user1.
  421. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  422. self.assertEqual(len(s["results"]), 1)
  423. async def allow_all(user_profile: ProfileInfo) -> bool:
  424. # Allow all users.
  425. return False
  426. # Configure a spam checker that does not filter any users.
  427. spam_checker = self.hs.get_spam_checker()
  428. spam_checker._check_username_for_spam_callbacks = [allow_all]
  429. # The results do not change:
  430. # We get one search result when searching for user2 by user1.
  431. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  432. self.assertEqual(len(s["results"]), 1)
  433. # Configure a spam checker that filters all users.
  434. async def block_all(user_profile: ProfileInfo) -> bool:
  435. # All users are spammy.
  436. return True
  437. spam_checker._check_username_for_spam_callbacks = [block_all]
  438. # User1 now gets no search results for any of the other users.
  439. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  440. self.assertEqual(len(s["results"]), 0)
  441. def test_legacy_spam_checker(self) -> None:
  442. """
  443. A spam checker without the expected method should be ignored.
  444. """
  445. u1 = self.register_user("user1", "pass")
  446. u1_token = self.login(u1, "pass")
  447. u2 = self.register_user("user2", "pass")
  448. u2_token = self.login(u2, "pass")
  449. # We do not add users to the directory until they join a room.
  450. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  451. self.assertEqual(len(s["results"]), 0)
  452. room = self.helper.create_room_as(u1, is_public=False, tok=u1_token)
  453. self.helper.invite(room, src=u1, targ=u2, tok=u1_token)
  454. self.helper.join(room, user=u2, tok=u2_token)
  455. # Check we have populated the database correctly.
  456. shares_private = self.get_success(
  457. self.user_dir_helper.get_users_who_share_private_rooms()
  458. )
  459. public_users = self.get_success(
  460. self.user_dir_helper.get_users_in_public_rooms()
  461. )
  462. self.assertEqual(
  463. self.user_dir_helper._compress_shared(shares_private),
  464. {(u1, u2, room), (u2, u1, room)},
  465. )
  466. self.assertEqual(public_users, [])
  467. # Configure a spam checker.
  468. spam_checker = self.hs.get_spam_checker()
  469. # The spam checker doesn't need any methods, so create a bare object.
  470. spam_checker.spam_checker = object()
  471. # We get one search result when searching for user2 by user1.
  472. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  473. self.assertEqual(len(s["results"]), 1)
  474. def test_initial_share_all_users(self) -> None:
  475. """
  476. Search all users = True means that a user does not have to share a
  477. private room with the searching user or be in a public room to be search
  478. visible.
  479. """
  480. self.handler.search_all_users = True
  481. self.hs.config.userdirectory.user_directory_search_all_users = True
  482. u1 = self.register_user("user1", "pass")
  483. self.register_user("user2", "pass")
  484. u3 = self.register_user("user3", "pass")
  485. shares_private = self.get_success(
  486. self.user_dir_helper.get_users_who_share_private_rooms()
  487. )
  488. public_users = self.get_success(
  489. self.user_dir_helper.get_users_in_public_rooms()
  490. )
  491. # No users share rooms
  492. self.assertEqual(public_users, [])
  493. self.assertEqual(self.user_dir_helper._compress_shared(shares_private), set())
  494. # Despite not sharing a room, search_all_users means we get a search
  495. # result.
  496. s = self.get_success(self.handler.search_users(u1, u3, 10))
  497. self.assertEqual(len(s["results"]), 1)
  498. # We can find the other two users
  499. s = self.get_success(self.handler.search_users(u1, "user", 10))
  500. self.assertEqual(len(s["results"]), 2)
  501. # Registering a user and then searching for them works.
  502. u4 = self.register_user("user4", "pass")
  503. s = self.get_success(self.handler.search_users(u1, u4, 10))
  504. self.assertEqual(len(s["results"]), 1)
  505. @override_config(
  506. {
  507. "user_directory": {
  508. "enabled": True,
  509. "search_all_users": True,
  510. "prefer_local_users": True,
  511. }
  512. }
  513. )
  514. def test_prefer_local_users(self) -> None:
  515. """Tests that local users are shown higher in search results when
  516. user_directory.prefer_local_users is True.
  517. """
  518. # Create a room and few users to test the directory with
  519. searching_user = self.register_user("searcher", "password")
  520. searching_user_tok = self.login("searcher", "password")
  521. room_id = self.helper.create_room_as(
  522. searching_user,
  523. room_version=RoomVersions.V1.identifier,
  524. tok=searching_user_tok,
  525. )
  526. # Create a few local users and join them to the room
  527. local_user_1 = self.register_user("user_xxxxx", "password")
  528. local_user_2 = self.register_user("user_bbbbb", "password")
  529. local_user_3 = self.register_user("user_zzzzz", "password")
  530. self._add_user_to_room(room_id, RoomVersions.V1, local_user_1)
  531. self._add_user_to_room(room_id, RoomVersions.V1, local_user_2)
  532. self._add_user_to_room(room_id, RoomVersions.V1, local_user_3)
  533. # Create a few "remote" users and join them to the room
  534. remote_user_1 = "@user_aaaaa:remote_server"
  535. remote_user_2 = "@user_yyyyy:remote_server"
  536. remote_user_3 = "@user_ccccc:remote_server"
  537. self._add_user_to_room(room_id, RoomVersions.V1, remote_user_1)
  538. self._add_user_to_room(room_id, RoomVersions.V1, remote_user_2)
  539. self._add_user_to_room(room_id, RoomVersions.V1, remote_user_3)
  540. local_users = [local_user_1, local_user_2, local_user_3]
  541. remote_users = [remote_user_1, remote_user_2, remote_user_3]
  542. # The local searching user searches for the term "user", which other users have
  543. # in their user id
  544. results = self.get_success(
  545. self.handler.search_users(searching_user, "user", 20)
  546. )["results"]
  547. received_user_id_ordering = [result["user_id"] for result in results]
  548. # Typically we'd expect Synapse to return users in lexicographical order,
  549. # assuming they have similar User IDs/display names, and profile information.
  550. # Check that the order of returned results using our module is as we expect,
  551. # i.e our local users show up first, despite all users having lexographically mixed
  552. # user IDs.
  553. [self.assertIn(user, local_users) for user in received_user_id_ordering[:3]]
  554. [self.assertIn(user, remote_users) for user in received_user_id_ordering[3:]]
  555. def _add_user_to_room(
  556. self,
  557. room_id: str,
  558. room_version: RoomVersion,
  559. user_id: str,
  560. ) -> None:
  561. # Add a user to the room.
  562. builder = self.event_builder_factory.for_room_version(
  563. room_version,
  564. {
  565. "type": "m.room.member",
  566. "sender": user_id,
  567. "state_key": user_id,
  568. "room_id": room_id,
  569. "content": {"membership": "join"},
  570. },
  571. )
  572. event, context = self.get_success(
  573. self.event_creation_handler.create_new_client_event(builder)
  574. )
  575. self.get_success(
  576. self.hs.get_storage().persistence.persist_event(event, context)
  577. )
  578. class TestUserDirSearchDisabled(unittest.HomeserverTestCase):
  579. servlets = [
  580. user_directory.register_servlets,
  581. room.register_servlets,
  582. login.register_servlets,
  583. synapse.rest.admin.register_servlets_for_client_rest_resource,
  584. ]
  585. def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
  586. config = self.default_config()
  587. config["update_user_directory"] = True
  588. hs = self.setup_test_homeserver(config=config)
  589. self.config = hs.config
  590. return hs
  591. def test_disabling_room_list(self) -> None:
  592. self.config.userdirectory.user_directory_search_enabled = True
  593. # Create two users and put them in the same room.
  594. u1 = self.register_user("user1", "pass")
  595. u1_token = self.login(u1, "pass")
  596. u2 = self.register_user("user2", "pass")
  597. u2_token = self.login(u2, "pass")
  598. room = self.helper.create_room_as(u1, tok=u1_token)
  599. self.helper.join(room, user=u2, tok=u2_token)
  600. # Each should see the other when searching the user directory.
  601. channel = self.make_request(
  602. "POST",
  603. b"user_directory/search",
  604. b'{"search_term":"user2"}',
  605. access_token=u1_token,
  606. )
  607. self.assertEquals(200, channel.code, channel.result)
  608. self.assertTrue(len(channel.json_body["results"]) > 0)
  609. # Disable user directory and check search returns nothing
  610. self.config.userdirectory.user_directory_search_enabled = False
  611. channel = self.make_request(
  612. "POST",
  613. b"user_directory/search",
  614. b'{"search_term":"user2"}',
  615. access_token=u1_token,
  616. )
  617. self.assertEquals(200, channel.code, channel.result)
  618. self.assertTrue(len(channel.json_body["results"]) == 0)