1
0

test_user_directory.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2018 New Vector
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from mock import Mock
  16. import synapse.rest.admin
  17. from synapse.api.constants import UserTypes
  18. from synapse.rest.client.v1 import login, room
  19. from synapse.rest.client.v2_alpha import user_directory
  20. from synapse.storage.roommember import ProfileInfo
  21. from tests import unittest
  22. class UserDirectoryTestCase(unittest.HomeserverTestCase):
  23. """
  24. Tests the UserDirectoryHandler.
  25. """
  26. servlets = [
  27. login.register_servlets,
  28. synapse.rest.admin.register_servlets_for_client_rest_resource,
  29. room.register_servlets,
  30. ]
  31. def make_homeserver(self, reactor, clock):
  32. config = self.default_config()
  33. config["update_user_directory"] = True
  34. return self.setup_test_homeserver(config=config)
  35. def prepare(self, reactor, clock, hs):
  36. self.store = hs.get_datastore()
  37. self.handler = hs.get_user_directory_handler()
  38. def test_handle_local_profile_change_with_support_user(self):
  39. support_user_id = "@support:test"
  40. self.get_success(
  41. self.store.register(
  42. user_id=support_user_id,
  43. token="123",
  44. password_hash=None,
  45. user_type=UserTypes.SUPPORT,
  46. )
  47. )
  48. self.get_success(
  49. self.handler.handle_local_profile_change(support_user_id, None)
  50. )
  51. profile = self.get_success(self.store.get_user_in_directory(support_user_id))
  52. self.assertTrue(profile is None)
  53. display_name = 'display_name'
  54. profile_info = ProfileInfo(avatar_url='avatar_url', display_name=display_name)
  55. regular_user_id = '@regular:test'
  56. self.get_success(
  57. self.handler.handle_local_profile_change(regular_user_id, profile_info)
  58. )
  59. profile = self.get_success(self.store.get_user_in_directory(regular_user_id))
  60. self.assertTrue(profile['display_name'] == display_name)
  61. def test_handle_user_deactivated_support_user(self):
  62. s_user_id = "@support:test"
  63. self.get_success(
  64. self.store.register(
  65. user_id=s_user_id,
  66. token="123",
  67. password_hash=None,
  68. user_type=UserTypes.SUPPORT,
  69. )
  70. )
  71. self.store.remove_from_user_dir = Mock()
  72. self.store.remove_from_user_in_public_room = Mock()
  73. self.get_success(self.handler.handle_user_deactivated(s_user_id))
  74. self.store.remove_from_user_dir.not_called()
  75. self.store.remove_from_user_in_public_room.not_called()
  76. def test_handle_user_deactivated_regular_user(self):
  77. r_user_id = "@regular:test"
  78. self.get_success(
  79. self.store.register(user_id=r_user_id, token="123", password_hash=None)
  80. )
  81. self.store.remove_from_user_dir = Mock()
  82. self.get_success(self.handler.handle_user_deactivated(r_user_id))
  83. self.store.remove_from_user_dir.called_once_with(r_user_id)
  84. def test_private_room(self):
  85. """
  86. A user can be searched for only by people that are either in a public
  87. room, or that share a private chat.
  88. """
  89. u1 = self.register_user("user1", "pass")
  90. u1_token = self.login(u1, "pass")
  91. u2 = self.register_user("user2", "pass")
  92. u2_token = self.login(u2, "pass")
  93. u3 = self.register_user("user3", "pass")
  94. # We do not add users to the directory until they join a room.
  95. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  96. self.assertEqual(len(s["results"]), 0)
  97. room = self.helper.create_room_as(u1, is_public=False, tok=u1_token)
  98. self.helper.invite(room, src=u1, targ=u2, tok=u1_token)
  99. self.helper.join(room, user=u2, tok=u2_token)
  100. # Check we have populated the database correctly.
  101. shares_private = self.get_users_who_share_private_rooms()
  102. public_users = self.get_users_in_public_rooms()
  103. self.assertEqual(
  104. self._compress_shared(shares_private), set([(u1, u2, room), (u2, u1, room)])
  105. )
  106. self.assertEqual(public_users, [])
  107. # We get one search result when searching for user2 by user1.
  108. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  109. self.assertEqual(len(s["results"]), 1)
  110. # We get NO search results when searching for user2 by user3.
  111. s = self.get_success(self.handler.search_users(u3, "user2", 10))
  112. self.assertEqual(len(s["results"]), 0)
  113. # We get NO search results when searching for user3 by user1.
  114. s = self.get_success(self.handler.search_users(u1, "user3", 10))
  115. self.assertEqual(len(s["results"]), 0)
  116. # User 2 then leaves.
  117. self.helper.leave(room, user=u2, tok=u2_token)
  118. # Check we have removed the values.
  119. shares_private = self.get_users_who_share_private_rooms()
  120. public_users = self.get_users_in_public_rooms()
  121. self.assertEqual(self._compress_shared(shares_private), set())
  122. self.assertEqual(public_users, [])
  123. # User1 now gets no search results for any of the other users.
  124. s = self.get_success(self.handler.search_users(u1, "user2", 10))
  125. self.assertEqual(len(s["results"]), 0)
  126. s = self.get_success(self.handler.search_users(u1, "user3", 10))
  127. self.assertEqual(len(s["results"]), 0)
  128. def _compress_shared(self, shared):
  129. """
  130. Compress a list of users who share rooms dicts to a list of tuples.
  131. """
  132. r = set()
  133. for i in shared:
  134. r.add((i["user_id"], i["other_user_id"], i["room_id"]))
  135. return r
  136. def get_users_in_public_rooms(self):
  137. r = self.get_success(
  138. self.store._simple_select_list(
  139. "users_in_public_rooms", None, ("user_id", "room_id")
  140. )
  141. )
  142. retval = []
  143. for i in r:
  144. retval.append((i["user_id"], i["room_id"]))
  145. return retval
  146. def get_users_who_share_private_rooms(self):
  147. return self.get_success(
  148. self.store._simple_select_list(
  149. "users_who_share_private_rooms",
  150. None,
  151. ["user_id", "other_user_id", "room_id"],
  152. )
  153. )
  154. def _add_background_updates(self):
  155. """
  156. Add the background updates we need to run.
  157. """
  158. # Ugh, have to reset this flag
  159. self.store._all_done = False
  160. self.get_success(
  161. self.store._simple_insert(
  162. "background_updates",
  163. {
  164. "update_name": "populate_user_directory_createtables",
  165. "progress_json": "{}",
  166. },
  167. )
  168. )
  169. self.get_success(
  170. self.store._simple_insert(
  171. "background_updates",
  172. {
  173. "update_name": "populate_user_directory_process_rooms",
  174. "progress_json": "{}",
  175. "depends_on": "populate_user_directory_createtables",
  176. },
  177. )
  178. )
  179. self.get_success(
  180. self.store._simple_insert(
  181. "background_updates",
  182. {
  183. "update_name": "populate_user_directory_process_users",
  184. "progress_json": "{}",
  185. "depends_on": "populate_user_directory_process_rooms",
  186. },
  187. )
  188. )
  189. self.get_success(
  190. self.store._simple_insert(
  191. "background_updates",
  192. {
  193. "update_name": "populate_user_directory_cleanup",
  194. "progress_json": "{}",
  195. "depends_on": "populate_user_directory_process_users",
  196. },
  197. )
  198. )
  199. def test_initial(self):
  200. """
  201. The user directory's initial handler correctly updates the search tables.
  202. """
  203. u1 = self.register_user("user1", "pass")
  204. u1_token = self.login(u1, "pass")
  205. u2 = self.register_user("user2", "pass")
  206. u2_token = self.login(u2, "pass")
  207. u3 = self.register_user("user3", "pass")
  208. u3_token = self.login(u3, "pass")
  209. room = self.helper.create_room_as(u1, is_public=True, tok=u1_token)
  210. self.helper.invite(room, src=u1, targ=u2, tok=u1_token)
  211. self.helper.join(room, user=u2, tok=u2_token)
  212. private_room = self.helper.create_room_as(u1, is_public=False, tok=u1_token)
  213. self.helper.invite(private_room, src=u1, targ=u3, tok=u1_token)
  214. self.helper.join(private_room, user=u3, tok=u3_token)
  215. self.get_success(self.store.update_user_directory_stream_pos(None))
  216. self.get_success(self.store.delete_all_from_user_dir())
  217. shares_private = self.get_users_who_share_private_rooms()
  218. public_users = self.get_users_in_public_rooms()
  219. # Nothing updated yet
  220. self.assertEqual(shares_private, [])
  221. self.assertEqual(public_users, [])
  222. # Do the initial population of the user directory via the background update
  223. self._add_background_updates()
  224. while not self.get_success(self.store.has_completed_background_updates()):
  225. self.get_success(self.store.do_next_background_update(100), by=0.1)
  226. shares_private = self.get_users_who_share_private_rooms()
  227. public_users = self.get_users_in_public_rooms()
  228. # User 1 and User 2 are in the same public room
  229. self.assertEqual(set(public_users), set([(u1, room), (u2, room)]))
  230. # User 1 and User 3 share private rooms
  231. self.assertEqual(
  232. self._compress_shared(shares_private),
  233. set([(u1, u3, private_room), (u3, u1, private_room)]),
  234. )
  235. def test_initial_share_all_users(self):
  236. """
  237. Search all users = True means that a user does not have to share a
  238. private room with the searching user or be in a public room to be search
  239. visible.
  240. """
  241. self.handler.search_all_users = True
  242. self.hs.config.user_directory_search_all_users = True
  243. u1 = self.register_user("user1", "pass")
  244. self.register_user("user2", "pass")
  245. u3 = self.register_user("user3", "pass")
  246. # Wipe the user dir
  247. self.get_success(self.store.update_user_directory_stream_pos(None))
  248. self.get_success(self.store.delete_all_from_user_dir())
  249. # Do the initial population of the user directory via the background update
  250. self._add_background_updates()
  251. while not self.get_success(self.store.has_completed_background_updates()):
  252. self.get_success(self.store.do_next_background_update(100), by=0.1)
  253. shares_private = self.get_users_who_share_private_rooms()
  254. public_users = self.get_users_in_public_rooms()
  255. # No users share rooms
  256. self.assertEqual(public_users, [])
  257. self.assertEqual(self._compress_shared(shares_private), set([]))
  258. # Despite not sharing a room, search_all_users means we get a search
  259. # result.
  260. s = self.get_success(self.handler.search_users(u1, u3, 10))
  261. self.assertEqual(len(s["results"]), 1)
  262. # We can find the other two users
  263. s = self.get_success(self.handler.search_users(u1, "user", 10))
  264. self.assertEqual(len(s["results"]), 2)
  265. # Registering a user and then searching for them works.
  266. u4 = self.register_user("user4", "pass")
  267. s = self.get_success(self.handler.search_users(u1, u4, 10))
  268. self.assertEqual(len(s["results"]), 1)
  269. class TestUserDirSearchDisabled(unittest.HomeserverTestCase):
  270. user_id = "@test:test"
  271. servlets = [
  272. user_directory.register_servlets,
  273. room.register_servlets,
  274. login.register_servlets,
  275. synapse.rest.admin.register_servlets_for_client_rest_resource,
  276. ]
  277. def make_homeserver(self, reactor, clock):
  278. config = self.default_config()
  279. config["update_user_directory"] = True
  280. hs = self.setup_test_homeserver(config=config)
  281. self.config = hs.config
  282. return hs
  283. def test_disabling_room_list(self):
  284. self.config.user_directory_search_enabled = True
  285. # First we create a room with another user so that user dir is non-empty
  286. # for our user
  287. self.helper.create_room_as(self.user_id)
  288. u2 = self.register_user("user2", "pass")
  289. room = self.helper.create_room_as(self.user_id)
  290. self.helper.join(room, user=u2)
  291. # Assert user directory is not empty
  292. request, channel = self.make_request(
  293. "POST", b"user_directory/search", b'{"search_term":"user2"}'
  294. )
  295. self.render(request)
  296. self.assertEquals(200, channel.code, channel.result)
  297. self.assertTrue(len(channel.json_body["results"]) > 0)
  298. # Disable user directory and check search returns nothing
  299. self.config.user_directory_search_enabled = False
  300. request, channel = self.make_request(
  301. "POST", b"user_directory/search", b'{"search_term":"user2"}'
  302. )
  303. self.render(request)
  304. self.assertEquals(200, channel.code, channel.result)
  305. self.assertTrue(len(channel.json_body["results"]) == 0)