test_register.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015, 2016 OpenMarket Ltd
  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. from synapse.api.auth import Auth
  17. from synapse.api.constants import UserTypes
  18. from synapse.api.errors import Codes, ResourceLimitError, SynapseError
  19. from synapse.spam_checker_api import RegistrationBehaviour
  20. from synapse.types import RoomAlias, UserID, create_requester
  21. from tests.test_utils import make_awaitable
  22. from tests.unittest import override_config
  23. from tests.utils import mock_getRawHeaders
  24. from .. import unittest
  25. class RegistrationTestCase(unittest.HomeserverTestCase):
  26. """ Tests the RegistrationHandler. """
  27. def make_homeserver(self, reactor, clock):
  28. hs_config = self.default_config()
  29. # some of the tests rely on us having a user consent version
  30. hs_config["user_consent"] = {
  31. "version": "test_consent_version",
  32. "template_dir": ".",
  33. }
  34. hs_config["max_mau_value"] = 50
  35. hs_config["limit_usage_by_mau"] = True
  36. hs = self.setup_test_homeserver(config=hs_config)
  37. return hs
  38. def prepare(self, reactor, clock, hs):
  39. self.mock_distributor = Mock()
  40. self.mock_distributor.declare("registered_user")
  41. self.mock_captcha_client = Mock()
  42. self.macaroon_generator = Mock(
  43. generate_access_token=Mock(return_value="secret")
  44. )
  45. self.hs.get_macaroon_generator = Mock(return_value=self.macaroon_generator)
  46. self.handler = self.hs.get_registration_handler()
  47. self.store = self.hs.get_datastore()
  48. self.lots_of_users = 100
  49. self.small_number_of_users = 1
  50. self.requester = create_requester("@requester:test")
  51. def test_user_is_created_and_logged_in_if_doesnt_exist(self):
  52. frank = UserID.from_string("@frank:test")
  53. user_id = frank.to_string()
  54. requester = create_requester(user_id)
  55. result_user_id, result_token = self.get_success(
  56. self.get_or_create_user(requester, frank.localpart, "Frankie")
  57. )
  58. self.assertEquals(result_user_id, user_id)
  59. self.assertTrue(result_token is not None)
  60. self.assertEquals(result_token, "secret")
  61. def test_if_user_exists(self):
  62. store = self.hs.get_datastore()
  63. frank = UserID.from_string("@frank:test")
  64. self.get_success(
  65. store.register_user(user_id=frank.to_string(), password_hash=None)
  66. )
  67. local_part = frank.localpart
  68. user_id = frank.to_string()
  69. requester = create_requester(user_id)
  70. result_user_id, result_token = self.get_success(
  71. self.get_or_create_user(requester, local_part, None)
  72. )
  73. self.assertEquals(result_user_id, user_id)
  74. self.assertTrue(result_token is not None)
  75. def test_mau_limits_when_disabled(self):
  76. self.hs.config.limit_usage_by_mau = False
  77. # Ensure does not throw exception
  78. self.get_success(self.get_or_create_user(self.requester, "a", "display_name"))
  79. def test_get_or_create_user_mau_not_blocked(self):
  80. self.hs.config.limit_usage_by_mau = True
  81. self.store.count_monthly_users = Mock(
  82. return_value=make_awaitable(self.hs.config.max_mau_value - 1)
  83. )
  84. # Ensure does not throw exception
  85. self.get_success(self.get_or_create_user(self.requester, "c", "User"))
  86. def test_get_or_create_user_mau_blocked(self):
  87. self.hs.config.limit_usage_by_mau = True
  88. self.store.get_monthly_active_count = Mock(
  89. return_value=make_awaitable(self.lots_of_users)
  90. )
  91. self.get_failure(
  92. self.get_or_create_user(self.requester, "b", "display_name"),
  93. ResourceLimitError,
  94. )
  95. self.store.get_monthly_active_count = Mock(
  96. return_value=make_awaitable(self.hs.config.max_mau_value)
  97. )
  98. self.get_failure(
  99. self.get_or_create_user(self.requester, "b", "display_name"),
  100. ResourceLimitError,
  101. )
  102. def test_register_mau_blocked(self):
  103. self.hs.config.limit_usage_by_mau = True
  104. self.store.get_monthly_active_count = Mock(
  105. return_value=make_awaitable(self.lots_of_users)
  106. )
  107. self.get_failure(
  108. self.handler.register_user(localpart="local_part"), ResourceLimitError
  109. )
  110. self.store.get_monthly_active_count = Mock(
  111. return_value=make_awaitable(self.hs.config.max_mau_value)
  112. )
  113. self.get_failure(
  114. self.handler.register_user(localpart="local_part"), ResourceLimitError
  115. )
  116. def test_auto_join_rooms_for_guests(self):
  117. room_alias_str = "#room:test"
  118. self.hs.config.auto_join_rooms = [room_alias_str]
  119. self.hs.config.auto_join_rooms_for_guests = False
  120. user_id = self.get_success(
  121. self.handler.register_user(localpart="jeff", make_guest=True),
  122. )
  123. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  124. self.assertEqual(len(rooms), 0)
  125. @override_config({"auto_join_rooms": ["#room:test"]})
  126. def test_auto_create_auto_join_rooms(self):
  127. room_alias_str = "#room:test"
  128. user_id = self.get_success(self.handler.register_user(localpart="jeff"))
  129. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  130. directory_handler = self.hs.get_directory_handler()
  131. room_alias = RoomAlias.from_string(room_alias_str)
  132. room_id = self.get_success(directory_handler.get_association(room_alias))
  133. self.assertTrue(room_id["room_id"] in rooms)
  134. self.assertEqual(len(rooms), 1)
  135. def test_auto_create_auto_join_rooms_with_no_rooms(self):
  136. self.hs.config.auto_join_rooms = []
  137. frank = UserID.from_string("@frank:test")
  138. user_id = self.get_success(self.handler.register_user(frank.localpart))
  139. self.assertEqual(user_id, frank.to_string())
  140. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  141. self.assertEqual(len(rooms), 0)
  142. def test_auto_create_auto_join_where_room_is_another_domain(self):
  143. self.hs.config.auto_join_rooms = ["#room:another"]
  144. frank = UserID.from_string("@frank:test")
  145. user_id = self.get_success(self.handler.register_user(frank.localpart))
  146. self.assertEqual(user_id, frank.to_string())
  147. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  148. self.assertEqual(len(rooms), 0)
  149. def test_auto_create_auto_join_where_auto_create_is_false(self):
  150. self.hs.config.autocreate_auto_join_rooms = False
  151. room_alias_str = "#room:test"
  152. self.hs.config.auto_join_rooms = [room_alias_str]
  153. user_id = self.get_success(self.handler.register_user(localpart="jeff"))
  154. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  155. self.assertEqual(len(rooms), 0)
  156. def test_auto_create_auto_join_rooms_when_user_is_not_a_real_user(self):
  157. room_alias_str = "#room:test"
  158. self.hs.config.auto_join_rooms = [room_alias_str]
  159. self.store.is_real_user = Mock(return_value=make_awaitable(False))
  160. user_id = self.get_success(self.handler.register_user(localpart="support"))
  161. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  162. self.assertEqual(len(rooms), 0)
  163. directory_handler = self.hs.get_directory_handler()
  164. room_alias = RoomAlias.from_string(room_alias_str)
  165. self.get_failure(directory_handler.get_association(room_alias), SynapseError)
  166. @override_config({"auto_join_rooms": ["#room:test"]})
  167. def test_auto_create_auto_join_rooms_when_user_is_the_first_real_user(self):
  168. room_alias_str = "#room:test"
  169. self.store.count_real_users = Mock(return_value=make_awaitable(1))
  170. self.store.is_real_user = Mock(return_value=make_awaitable(True))
  171. user_id = self.get_success(self.handler.register_user(localpart="real"))
  172. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  173. directory_handler = self.hs.get_directory_handler()
  174. room_alias = RoomAlias.from_string(room_alias_str)
  175. room_id = self.get_success(directory_handler.get_association(room_alias))
  176. self.assertTrue(room_id["room_id"] in rooms)
  177. self.assertEqual(len(rooms), 1)
  178. def test_auto_create_auto_join_rooms_when_user_is_not_the_first_real_user(self):
  179. room_alias_str = "#room:test"
  180. self.hs.config.auto_join_rooms = [room_alias_str]
  181. self.store.count_real_users = Mock(return_value=make_awaitable(2))
  182. self.store.is_real_user = Mock(return_value=make_awaitable(True))
  183. user_id = self.get_success(self.handler.register_user(localpart="real"))
  184. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  185. self.assertEqual(len(rooms), 0)
  186. @override_config(
  187. {
  188. "auto_join_rooms": ["#room:test"],
  189. "autocreate_auto_join_rooms_federated": False,
  190. }
  191. )
  192. def test_auto_create_auto_join_rooms_federated(self):
  193. """
  194. Auto-created rooms that are private require an invite to go to the user
  195. (instead of directly joining it).
  196. """
  197. room_alias_str = "#room:test"
  198. user_id = self.get_success(self.handler.register_user(localpart="jeff"))
  199. # Ensure the room was created.
  200. directory_handler = self.hs.get_directory_handler()
  201. room_alias = RoomAlias.from_string(room_alias_str)
  202. room_id = self.get_success(directory_handler.get_association(room_alias))
  203. # Ensure the room is properly not federated.
  204. room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
  205. self.assertFalse(room["federatable"])
  206. self.assertFalse(room["public"])
  207. self.assertEqual(room["join_rules"], "public")
  208. self.assertIsNone(room["guest_access"])
  209. # The user should be in the room.
  210. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  211. self.assertIn(room_id["room_id"], rooms)
  212. @override_config(
  213. {"auto_join_rooms": ["#room:test"], "auto_join_mxid_localpart": "support"}
  214. )
  215. def test_auto_join_mxid_localpart(self):
  216. """
  217. Ensure the user still needs up in the room created by a different user.
  218. """
  219. # Ensure the support user exists.
  220. inviter = "@support:test"
  221. room_alias_str = "#room:test"
  222. user_id = self.get_success(self.handler.register_user(localpart="jeff"))
  223. # Ensure the room was created.
  224. directory_handler = self.hs.get_directory_handler()
  225. room_alias = RoomAlias.from_string(room_alias_str)
  226. room_id = self.get_success(directory_handler.get_association(room_alias))
  227. # Ensure the room is properly a public room.
  228. room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
  229. self.assertEqual(room["join_rules"], "public")
  230. # Both users should be in the room.
  231. rooms = self.get_success(self.store.get_rooms_for_user(inviter))
  232. self.assertIn(room_id["room_id"], rooms)
  233. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  234. self.assertIn(room_id["room_id"], rooms)
  235. # Register a second user, which should also end up in the room.
  236. user_id = self.get_success(self.handler.register_user(localpart="bob"))
  237. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  238. self.assertIn(room_id["room_id"], rooms)
  239. @override_config(
  240. {
  241. "auto_join_rooms": ["#room:test"],
  242. "autocreate_auto_join_room_preset": "private_chat",
  243. "auto_join_mxid_localpart": "support",
  244. }
  245. )
  246. def test_auto_create_auto_join_room_preset(self):
  247. """
  248. Auto-created rooms that are private require an invite to go to the user
  249. (instead of directly joining it).
  250. """
  251. # Ensure the support user exists.
  252. inviter = "@support:test"
  253. room_alias_str = "#room:test"
  254. user_id = self.get_success(self.handler.register_user(localpart="jeff"))
  255. # Ensure the room was created.
  256. directory_handler = self.hs.get_directory_handler()
  257. room_alias = RoomAlias.from_string(room_alias_str)
  258. room_id = self.get_success(directory_handler.get_association(room_alias))
  259. # Ensure the room is properly a private room.
  260. room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
  261. self.assertFalse(room["public"])
  262. self.assertEqual(room["join_rules"], "invite")
  263. self.assertEqual(room["guest_access"], "can_join")
  264. # Both users should be in the room.
  265. rooms = self.get_success(self.store.get_rooms_for_user(inviter))
  266. self.assertIn(room_id["room_id"], rooms)
  267. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  268. self.assertIn(room_id["room_id"], rooms)
  269. # Register a second user, which should also end up in the room.
  270. user_id = self.get_success(self.handler.register_user(localpart="bob"))
  271. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  272. self.assertIn(room_id["room_id"], rooms)
  273. @override_config(
  274. {
  275. "auto_join_rooms": ["#room:test"],
  276. "autocreate_auto_join_room_preset": "private_chat",
  277. "auto_join_mxid_localpart": "support",
  278. }
  279. )
  280. def test_auto_create_auto_join_room_preset_guest(self):
  281. """
  282. Auto-created rooms that are private require an invite to go to the user
  283. (instead of directly joining it).
  284. This should also work for guests.
  285. """
  286. inviter = "@support:test"
  287. room_alias_str = "#room:test"
  288. user_id = self.get_success(
  289. self.handler.register_user(localpart="jeff", make_guest=True)
  290. )
  291. # Ensure the room was created.
  292. directory_handler = self.hs.get_directory_handler()
  293. room_alias = RoomAlias.from_string(room_alias_str)
  294. room_id = self.get_success(directory_handler.get_association(room_alias))
  295. # Ensure the room is properly a private room.
  296. room = self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
  297. self.assertFalse(room["public"])
  298. self.assertEqual(room["join_rules"], "invite")
  299. self.assertEqual(room["guest_access"], "can_join")
  300. # Both users should be in the room.
  301. rooms = self.get_success(self.store.get_rooms_for_user(inviter))
  302. self.assertIn(room_id["room_id"], rooms)
  303. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  304. self.assertIn(room_id["room_id"], rooms)
  305. @override_config(
  306. {
  307. "auto_join_rooms": ["#room:test"],
  308. "autocreate_auto_join_room_preset": "private_chat",
  309. "auto_join_mxid_localpart": "support",
  310. }
  311. )
  312. def test_auto_create_auto_join_room_preset_invalid_permissions(self):
  313. """
  314. Auto-created rooms that are private require an invite, check that
  315. registration doesn't completely break if the inviter doesn't have proper
  316. permissions.
  317. """
  318. inviter = "@support:test"
  319. # Register an initial user to create the room and such (essentially this
  320. # is a subset of test_auto_create_auto_join_room_preset).
  321. room_alias_str = "#room:test"
  322. user_id = self.get_success(self.handler.register_user(localpart="jeff"))
  323. # Ensure the room was created.
  324. directory_handler = self.hs.get_directory_handler()
  325. room_alias = RoomAlias.from_string(room_alias_str)
  326. room_id = self.get_success(directory_handler.get_association(room_alias))
  327. # Ensure the room exists.
  328. self.get_success(self.store.get_room_with_stats(room_id["room_id"]))
  329. # Both users should be in the room.
  330. rooms = self.get_success(self.store.get_rooms_for_user(inviter))
  331. self.assertIn(room_id["room_id"], rooms)
  332. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  333. self.assertIn(room_id["room_id"], rooms)
  334. # Lower the permissions of the inviter.
  335. event_creation_handler = self.hs.get_event_creation_handler()
  336. requester = create_requester(inviter)
  337. event, context = self.get_success(
  338. event_creation_handler.create_event(
  339. requester,
  340. {
  341. "type": "m.room.power_levels",
  342. "state_key": "",
  343. "room_id": room_id["room_id"],
  344. "content": {"invite": 100, "users": {inviter: 0}},
  345. "sender": inviter,
  346. },
  347. )
  348. )
  349. self.get_success(
  350. event_creation_handler.handle_new_client_event(requester, event, context)
  351. )
  352. # Register a second user, which won't be be in the room (or even have an invite)
  353. # since the inviter no longer has the proper permissions.
  354. user_id = self.get_success(self.handler.register_user(localpart="bob"))
  355. # This user should not be in any rooms.
  356. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  357. invited_rooms = self.get_success(
  358. self.store.get_invited_rooms_for_local_user(user_id)
  359. )
  360. self.assertEqual(rooms, set())
  361. self.assertEqual(invited_rooms, [])
  362. def test_auto_create_auto_join_where_no_consent(self):
  363. """Test to ensure that the first user is not auto-joined to a room if
  364. they have not given general consent.
  365. """
  366. # Given:-
  367. # * a user must give consent,
  368. # * they have not given that consent
  369. # * The server is configured to auto-join to a room
  370. # (and autocreate if necessary)
  371. event_creation_handler = self.hs.get_event_creation_handler()
  372. # (Messing with the internals of event_creation_handler is fragile
  373. # but can't see a better way to do this. One option could be to subclass
  374. # the test with custom config.)
  375. event_creation_handler._block_events_without_consent_error = "Error"
  376. event_creation_handler._consent_uri_builder = Mock()
  377. room_alias_str = "#room:test"
  378. self.hs.config.auto_join_rooms = [room_alias_str]
  379. # When:-
  380. # * the user is registered and post consent actions are called
  381. user_id = self.get_success(self.handler.register_user(localpart="jeff"))
  382. self.get_success(self.handler.post_consent_actions(user_id))
  383. # Then:-
  384. # * Ensure that they have not been joined to the room
  385. rooms = self.get_success(self.store.get_rooms_for_user(user_id))
  386. self.assertEqual(len(rooms), 0)
  387. def test_register_support_user(self):
  388. user_id = self.get_success(
  389. self.handler.register_user(localpart="user", user_type=UserTypes.SUPPORT)
  390. )
  391. d = self.store.is_support_user(user_id)
  392. self.assertTrue(self.get_success(d))
  393. def test_register_not_support_user(self):
  394. user_id = self.get_success(self.handler.register_user(localpart="user"))
  395. d = self.store.is_support_user(user_id)
  396. self.assertFalse(self.get_success(d))
  397. def test_invalid_user_id_length(self):
  398. invalid_user_id = "x" * 256
  399. self.get_failure(
  400. self.handler.register_user(localpart=invalid_user_id), SynapseError
  401. )
  402. def test_spam_checker_deny(self):
  403. """A spam checker can deny registration, which results in an error."""
  404. class DenyAll:
  405. def check_registration_for_spam(
  406. self, email_threepid, username, request_info
  407. ):
  408. return RegistrationBehaviour.DENY
  409. # Configure a spam checker that denies all users.
  410. spam_checker = self.hs.get_spam_checker()
  411. spam_checker.spam_checkers = [DenyAll()]
  412. self.get_failure(self.handler.register_user(localpart="user"), SynapseError)
  413. def test_spam_checker_shadow_ban(self):
  414. """A spam checker can choose to shadow-ban a user, which allows registration to succeed."""
  415. class BanAll:
  416. def check_registration_for_spam(
  417. self, email_threepid, username, request_info
  418. ):
  419. return RegistrationBehaviour.SHADOW_BAN
  420. # Configure a spam checker that denies all users.
  421. spam_checker = self.hs.get_spam_checker()
  422. spam_checker.spam_checkers = [BanAll()]
  423. user_id = self.get_success(self.handler.register_user(localpart="user"))
  424. # Get an access token.
  425. token = self.macaroon_generator.generate_access_token(user_id)
  426. self.get_success(
  427. self.store.add_access_token_to_user(
  428. user_id=user_id, token=token, device_id=None, valid_until_ms=None
  429. )
  430. )
  431. # Ensure the user was marked as shadow-banned.
  432. request = Mock(args={})
  433. request.args[b"access_token"] = [token.encode("ascii")]
  434. request.requestHeaders.getRawHeaders = mock_getRawHeaders()
  435. auth = Auth(self.hs)
  436. requester = self.get_success(auth.get_user_by_req(request))
  437. self.assertTrue(requester.shadow_banned)
  438. async def get_or_create_user(
  439. self, requester, localpart, displayname, password_hash=None
  440. ):
  441. """Creates a new user if the user does not exist,
  442. else revokes all previous access tokens and generates a new one.
  443. XXX: this used to be in the main codebase, but was only used by this file,
  444. so got moved here. TODO: get rid of it, probably
  445. Args:
  446. localpart : The local part of the user ID to register. If None,
  447. one will be randomly generated.
  448. Returns:
  449. A tuple of (user_id, access_token).
  450. """
  451. if localpart is None:
  452. raise SynapseError(400, "Request must include user id")
  453. await self.hs.get_auth().check_auth_blocking()
  454. need_register = True
  455. try:
  456. await self.handler.check_username(localpart)
  457. except SynapseError as e:
  458. if e.errcode == Codes.USER_IN_USE:
  459. need_register = False
  460. else:
  461. raise
  462. user = UserID(localpart, self.hs.hostname)
  463. user_id = user.to_string()
  464. token = self.macaroon_generator.generate_access_token(user_id)
  465. if need_register:
  466. await self.handler.register_with_store(
  467. user_id=user_id,
  468. password_hash=password_hash,
  469. create_profile_with_displayname=user.localpart,
  470. )
  471. else:
  472. await self.hs.get_auth_handler().delete_access_tokens_for_user(user_id)
  473. await self.store.add_access_token_to_user(
  474. user_id=user_id, token=token, device_id=None, valid_until_ms=None
  475. )
  476. if displayname is not None:
  477. # logger.info("setting user display name: %s -> %s", user_id, displayname)
  478. await self.hs.get_profile_handler().set_displayname(
  479. user, requester, displayname, by_admin=True
  480. )
  481. return user_id, token