register.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014 - 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. """Contains functions for registering clients."""
  16. import logging
  17. from synapse import types
  18. from synapse.api.constants import MAX_USERID_LENGTH, LoginType
  19. from synapse.api.errors import AuthError, Codes, ConsentNotGivenError, SynapseError
  20. from synapse.config.server import is_threepid_reserved
  21. from synapse.http.servlet import assert_params_in_dict
  22. from synapse.replication.http.login import RegisterDeviceReplicationServlet
  23. from synapse.replication.http.register import (
  24. ReplicationPostRegisterActionsServlet,
  25. ReplicationRegisterServlet,
  26. )
  27. from synapse.types import RoomAlias, RoomID, UserID, create_requester
  28. from synapse.util.async_helpers import Linearizer
  29. from ._base import BaseHandler
  30. logger = logging.getLogger(__name__)
  31. class RegistrationHandler(BaseHandler):
  32. def __init__(self, hs):
  33. """
  34. Args:
  35. hs (synapse.server.HomeServer):
  36. """
  37. super(RegistrationHandler, self).__init__(hs)
  38. self.hs = hs
  39. self.auth = hs.get_auth()
  40. self._auth_handler = hs.get_auth_handler()
  41. self.profile_handler = hs.get_profile_handler()
  42. self.user_directory_handler = hs.get_user_directory_handler()
  43. self.identity_handler = self.hs.get_handlers().identity_handler
  44. self.ratelimiter = hs.get_registration_ratelimiter()
  45. self._next_generated_user_id = None
  46. self.macaroon_gen = hs.get_macaroon_generator()
  47. self._generate_user_id_linearizer = Linearizer(
  48. name="_generate_user_id_linearizer"
  49. )
  50. self._server_notices_mxid = hs.config.server_notices_mxid
  51. if hs.config.worker_app:
  52. self._register_client = ReplicationRegisterServlet.make_client(hs)
  53. self._register_device_client = RegisterDeviceReplicationServlet.make_client(
  54. hs
  55. )
  56. self._post_registration_client = ReplicationPostRegisterActionsServlet.make_client(
  57. hs
  58. )
  59. else:
  60. self.device_handler = hs.get_device_handler()
  61. self.pusher_pool = hs.get_pusherpool()
  62. self.session_lifetime = hs.config.session_lifetime
  63. async def check_username(
  64. self, localpart, guest_access_token=None, assigned_user_id=None
  65. ):
  66. if types.contains_invalid_mxid_characters(localpart):
  67. raise SynapseError(
  68. 400,
  69. "User ID can only contain characters a-z, 0-9, or '=_-./'",
  70. Codes.INVALID_USERNAME,
  71. )
  72. if not localpart:
  73. raise SynapseError(400, "User ID cannot be empty", Codes.INVALID_USERNAME)
  74. if localpart[0] == "_":
  75. raise SynapseError(
  76. 400, "User ID may not begin with _", Codes.INVALID_USERNAME
  77. )
  78. user = UserID(localpart, self.hs.hostname)
  79. user_id = user.to_string()
  80. if assigned_user_id:
  81. if user_id == assigned_user_id:
  82. return
  83. else:
  84. raise SynapseError(
  85. 400,
  86. "A different user ID has already been registered for this session",
  87. )
  88. self.check_user_id_not_appservice_exclusive(user_id)
  89. if len(user_id) > MAX_USERID_LENGTH:
  90. raise SynapseError(
  91. 400,
  92. "User ID may not be longer than %s characters" % (MAX_USERID_LENGTH,),
  93. Codes.INVALID_USERNAME,
  94. )
  95. users = await self.store.get_users_by_id_case_insensitive(user_id)
  96. if users:
  97. if not guest_access_token:
  98. raise SynapseError(
  99. 400, "User ID already taken.", errcode=Codes.USER_IN_USE
  100. )
  101. user_data = await self.auth.get_user_by_access_token(guest_access_token)
  102. if not user_data["is_guest"] or user_data["user"].localpart != localpart:
  103. raise AuthError(
  104. 403,
  105. "Cannot register taken user ID without valid guest "
  106. "credentials for that user.",
  107. errcode=Codes.FORBIDDEN,
  108. )
  109. if guest_access_token is None:
  110. try:
  111. int(localpart)
  112. raise SynapseError(
  113. 400, "Numeric user IDs are reserved for guest users."
  114. )
  115. except ValueError:
  116. pass
  117. async def register_user(
  118. self,
  119. localpart=None,
  120. password_hash=None,
  121. guest_access_token=None,
  122. make_guest=False,
  123. admin=False,
  124. threepid=None,
  125. user_type=None,
  126. default_display_name=None,
  127. address=None,
  128. bind_emails=[],
  129. by_admin=False,
  130. ):
  131. """Registers a new client on the server.
  132. Args:
  133. localpart: The local part of the user ID to register. If None,
  134. one will be generated.
  135. password_hash (str|None): The hashed password to assign to this user so they can
  136. login again. This can be None which means they cannot login again
  137. via a password (e.g. the user is an application service user).
  138. user_type (str|None): type of user. One of the values from
  139. api.constants.UserTypes, or None for a normal user.
  140. default_display_name (unicode|None): if set, the new user's displayname
  141. will be set to this. Defaults to 'localpart'.
  142. address (str|None): the IP address used to perform the registration.
  143. bind_emails (List[str]): list of emails to bind to this account.
  144. by_admin (bool): True if this registration is being made via the
  145. admin api, otherwise False.
  146. Returns:
  147. str: user_id
  148. Raises:
  149. SynapseError if there was a problem registering.
  150. """
  151. self.check_registration_ratelimit(address)
  152. # do not check_auth_blocking if the call is coming through the Admin API
  153. if not by_admin:
  154. await self.auth.check_auth_blocking(threepid=threepid)
  155. if localpart is not None:
  156. await self.check_username(localpart, guest_access_token=guest_access_token)
  157. was_guest = guest_access_token is not None
  158. user = UserID(localpart, self.hs.hostname)
  159. user_id = user.to_string()
  160. if was_guest:
  161. # If the user was a guest then they already have a profile
  162. default_display_name = None
  163. elif default_display_name is None:
  164. default_display_name = localpart
  165. await self.register_with_store(
  166. user_id=user_id,
  167. password_hash=password_hash,
  168. was_guest=was_guest,
  169. make_guest=make_guest,
  170. create_profile_with_displayname=default_display_name,
  171. admin=admin,
  172. user_type=user_type,
  173. address=address,
  174. )
  175. if self.hs.config.user_directory_search_all_users:
  176. profile = await self.store.get_profileinfo(localpart)
  177. await self.user_directory_handler.handle_local_profile_change(
  178. user_id, profile
  179. )
  180. else:
  181. # autogen a sequential user ID
  182. fail_count = 0
  183. user = None
  184. while not user:
  185. # Fail after being unable to find a suitable ID a few times
  186. if fail_count > 10:
  187. raise SynapseError(500, "Unable to find a suitable guest user ID")
  188. localpart = await self._generate_user_id()
  189. user = UserID(localpart, self.hs.hostname)
  190. user_id = user.to_string()
  191. self.check_user_id_not_appservice_exclusive(user_id)
  192. if default_display_name is None:
  193. default_display_name = localpart
  194. try:
  195. await self.register_with_store(
  196. user_id=user_id,
  197. password_hash=password_hash,
  198. make_guest=make_guest,
  199. create_profile_with_displayname=default_display_name,
  200. address=address,
  201. )
  202. # Successfully registered
  203. break
  204. except SynapseError:
  205. # if user id is taken, just generate another
  206. user = None
  207. user_id = None
  208. fail_count += 1
  209. if not self.hs.config.user_consent_at_registration:
  210. if not self.hs.config.auto_join_rooms_for_guests and make_guest:
  211. logger.info(
  212. "Skipping auto-join for %s because auto-join for guests is disabled",
  213. user_id,
  214. )
  215. else:
  216. await self._auto_join_rooms(user_id)
  217. else:
  218. logger.info(
  219. "Skipping auto-join for %s because consent is required at registration",
  220. user_id,
  221. )
  222. # Bind any specified emails to this account
  223. current_time = self.hs.get_clock().time_msec()
  224. for email in bind_emails:
  225. # generate threepid dict
  226. threepid_dict = {
  227. "medium": "email",
  228. "address": email,
  229. "validated_at": current_time,
  230. }
  231. # Bind email to new account
  232. await self._register_email_threepid(user_id, threepid_dict, None)
  233. return user_id
  234. async def _auto_join_rooms(self, user_id):
  235. """Automatically joins users to auto join rooms - creating the room in the first place
  236. if the user is the first to be created.
  237. Args:
  238. user_id(str): The user to join
  239. """
  240. # auto-join the user to any rooms we're supposed to dump them into
  241. fake_requester = create_requester(user_id)
  242. # try to create the room if we're the first real user on the server. Note
  243. # that an auto-generated support or bot user is not a real user and will never be
  244. # the user to create the room
  245. should_auto_create_rooms = False
  246. is_real_user = await self.store.is_real_user(user_id)
  247. if self.hs.config.autocreate_auto_join_rooms and is_real_user:
  248. count = await self.store.count_real_users()
  249. should_auto_create_rooms = count == 1
  250. for r in self.hs.config.auto_join_rooms:
  251. logger.info("Auto-joining %s to %s", user_id, r)
  252. try:
  253. if should_auto_create_rooms:
  254. room_alias = RoomAlias.from_string(r)
  255. if self.hs.hostname != room_alias.domain:
  256. logger.warning(
  257. "Cannot create room alias %s, "
  258. "it does not match server domain",
  259. r,
  260. )
  261. else:
  262. # create room expects the localpart of the room alias
  263. room_alias_localpart = room_alias.localpart
  264. # getting the RoomCreationHandler during init gives a dependency
  265. # loop
  266. await self.hs.get_room_creation_handler().create_room(
  267. fake_requester,
  268. config={
  269. "preset": "public_chat",
  270. "room_alias_name": room_alias_localpart,
  271. },
  272. ratelimit=False,
  273. )
  274. else:
  275. await self._join_user_to_room(fake_requester, r)
  276. except ConsentNotGivenError as e:
  277. # Technically not necessary to pull out this error though
  278. # moving away from bare excepts is a good thing to do.
  279. logger.error("Failed to join new user to %r: %r", r, e)
  280. except Exception as e:
  281. logger.error("Failed to join new user to %r: %r", r, e)
  282. async def post_consent_actions(self, user_id):
  283. """A series of registration actions that can only be carried out once consent
  284. has been granted
  285. Args:
  286. user_id (str): The user to join
  287. """
  288. await self._auto_join_rooms(user_id)
  289. async def appservice_register(self, user_localpart, as_token):
  290. user = UserID(user_localpart, self.hs.hostname)
  291. user_id = user.to_string()
  292. service = self.store.get_app_service_by_token(as_token)
  293. if not service:
  294. raise AuthError(403, "Invalid application service token.")
  295. if not service.is_interested_in_user(user_id):
  296. raise SynapseError(
  297. 400,
  298. "Invalid user localpart for this application service.",
  299. errcode=Codes.EXCLUSIVE,
  300. )
  301. service_id = service.id if service.is_exclusive_user(user_id) else None
  302. self.check_user_id_not_appservice_exclusive(user_id, allowed_appservice=service)
  303. await self.register_with_store(
  304. user_id=user_id,
  305. password_hash="",
  306. appservice_id=service_id,
  307. create_profile_with_displayname=user.localpart,
  308. )
  309. return user_id
  310. def check_user_id_not_appservice_exclusive(self, user_id, allowed_appservice=None):
  311. # don't allow people to register the server notices mxid
  312. if self._server_notices_mxid is not None:
  313. if user_id == self._server_notices_mxid:
  314. raise SynapseError(
  315. 400, "This user ID is reserved.", errcode=Codes.EXCLUSIVE
  316. )
  317. # valid user IDs must not clash with any user ID namespaces claimed by
  318. # application services.
  319. services = self.store.get_app_services()
  320. interested_services = [
  321. s
  322. for s in services
  323. if s.is_interested_in_user(user_id) and s != allowed_appservice
  324. ]
  325. for service in interested_services:
  326. if service.is_exclusive_user(user_id):
  327. raise SynapseError(
  328. 400,
  329. "This user ID is reserved by an application service.",
  330. errcode=Codes.EXCLUSIVE,
  331. )
  332. async def _generate_user_id(self):
  333. if self._next_generated_user_id is None:
  334. with await self._generate_user_id_linearizer.queue(()):
  335. if self._next_generated_user_id is None:
  336. self._next_generated_user_id = (
  337. await self.store.find_next_generated_user_id_localpart()
  338. )
  339. id = self._next_generated_user_id
  340. self._next_generated_user_id += 1
  341. return str(id)
  342. async def _join_user_to_room(self, requester, room_identifier):
  343. room_member_handler = self.hs.get_room_member_handler()
  344. if RoomID.is_valid(room_identifier):
  345. room_id = room_identifier
  346. elif RoomAlias.is_valid(room_identifier):
  347. room_alias = RoomAlias.from_string(room_identifier)
  348. room_id, remote_room_hosts = await room_member_handler.lookup_room_alias(
  349. room_alias
  350. )
  351. room_id = room_id.to_string()
  352. else:
  353. raise SynapseError(
  354. 400, "%s was not legal room ID or room alias" % (room_identifier,)
  355. )
  356. await room_member_handler.update_membership(
  357. requester=requester,
  358. target=requester.user,
  359. room_id=room_id,
  360. remote_room_hosts=remote_room_hosts,
  361. action="join",
  362. ratelimit=False,
  363. )
  364. def check_registration_ratelimit(self, address):
  365. """A simple helper method to check whether the registration rate limit has been hit
  366. for a given IP address
  367. Args:
  368. address (str|None): the IP address used to perform the registration. If this is
  369. None, no ratelimiting will be performed.
  370. Raises:
  371. LimitExceededError: If the rate limit has been exceeded.
  372. """
  373. if not address:
  374. return
  375. self.ratelimiter.ratelimit(address)
  376. def register_with_store(
  377. self,
  378. user_id,
  379. password_hash=None,
  380. was_guest=False,
  381. make_guest=False,
  382. appservice_id=None,
  383. create_profile_with_displayname=None,
  384. admin=False,
  385. user_type=None,
  386. address=None,
  387. ):
  388. """Register user in the datastore.
  389. Args:
  390. user_id (str): The desired user ID to register.
  391. password_hash (str|None): Optional. The password hash for this user.
  392. was_guest (bool): Optional. Whether this is a guest account being
  393. upgraded to a non-guest account.
  394. make_guest (boolean): True if the the new user should be guest,
  395. false to add a regular user account.
  396. appservice_id (str|None): The ID of the appservice registering the user.
  397. create_profile_with_displayname (unicode|None): Optionally create a
  398. profile for the user, setting their displayname to the given value
  399. admin (boolean): is an admin user?
  400. user_type (str|None): type of user. One of the values from
  401. api.constants.UserTypes, or None for a normal user.
  402. address (str|None): the IP address used to perform the registration.
  403. Returns:
  404. Deferred
  405. """
  406. if self.hs.config.worker_app:
  407. return self._register_client(
  408. user_id=user_id,
  409. password_hash=password_hash,
  410. was_guest=was_guest,
  411. make_guest=make_guest,
  412. appservice_id=appservice_id,
  413. create_profile_with_displayname=create_profile_with_displayname,
  414. admin=admin,
  415. user_type=user_type,
  416. address=address,
  417. )
  418. else:
  419. return self.store.register_user(
  420. user_id=user_id,
  421. password_hash=password_hash,
  422. was_guest=was_guest,
  423. make_guest=make_guest,
  424. appservice_id=appservice_id,
  425. create_profile_with_displayname=create_profile_with_displayname,
  426. admin=admin,
  427. user_type=user_type,
  428. )
  429. async def register_device(
  430. self, user_id, device_id, initial_display_name, is_guest=False
  431. ):
  432. """Register a device for a user and generate an access token.
  433. The access token will be limited by the homeserver's session_lifetime config.
  434. Args:
  435. user_id (str): full canonical @user:id
  436. device_id (str|None): The device ID to check, or None to generate
  437. a new one.
  438. initial_display_name (str|None): An optional display name for the
  439. device.
  440. is_guest (bool): Whether this is a guest account
  441. Returns:
  442. tuple[str, str]: Tuple of device ID and access token
  443. """
  444. if self.hs.config.worker_app:
  445. r = await self._register_device_client(
  446. user_id=user_id,
  447. device_id=device_id,
  448. initial_display_name=initial_display_name,
  449. is_guest=is_guest,
  450. )
  451. return r["device_id"], r["access_token"]
  452. valid_until_ms = None
  453. if self.session_lifetime is not None:
  454. if is_guest:
  455. raise Exception(
  456. "session_lifetime is not currently implemented for guest access"
  457. )
  458. valid_until_ms = self.clock.time_msec() + self.session_lifetime
  459. device_id = await self.device_handler.check_device_registered(
  460. user_id, device_id, initial_display_name
  461. )
  462. if is_guest:
  463. assert valid_until_ms is None
  464. access_token = self.macaroon_gen.generate_access_token(
  465. user_id, ["guest = true"]
  466. )
  467. else:
  468. access_token = await self._auth_handler.get_access_token_for_user_id(
  469. user_id, device_id=device_id, valid_until_ms=valid_until_ms
  470. )
  471. return (device_id, access_token)
  472. async def post_registration_actions(self, user_id, auth_result, access_token):
  473. """A user has completed registration
  474. Args:
  475. user_id (str): The user ID that consented
  476. auth_result (dict): The authenticated credentials of the newly
  477. registered user.
  478. access_token (str|None): The access token of the newly logged in
  479. device, or None if `inhibit_login` enabled.
  480. """
  481. if self.hs.config.worker_app:
  482. await self._post_registration_client(
  483. user_id=user_id, auth_result=auth_result, access_token=access_token
  484. )
  485. return
  486. if auth_result and LoginType.EMAIL_IDENTITY in auth_result:
  487. threepid = auth_result[LoginType.EMAIL_IDENTITY]
  488. # Necessary due to auth checks prior to the threepid being
  489. # written to the db
  490. if is_threepid_reserved(
  491. self.hs.config.mau_limits_reserved_threepids, threepid
  492. ):
  493. await self.store.upsert_monthly_active_user(user_id)
  494. await self._register_email_threepid(user_id, threepid, access_token)
  495. if auth_result and LoginType.MSISDN in auth_result:
  496. threepid = auth_result[LoginType.MSISDN]
  497. await self._register_msisdn_threepid(user_id, threepid)
  498. if auth_result and LoginType.TERMS in auth_result:
  499. await self._on_user_consented(user_id, self.hs.config.user_consent_version)
  500. async def _on_user_consented(self, user_id, consent_version):
  501. """A user consented to the terms on registration
  502. Args:
  503. user_id (str): The user ID that consented.
  504. consent_version (str): version of the policy the user has
  505. consented to.
  506. """
  507. logger.info("%s has consented to the privacy policy", user_id)
  508. await self.store.user_set_consent_version(user_id, consent_version)
  509. await self.post_consent_actions(user_id)
  510. async def _register_email_threepid(self, user_id, threepid, token):
  511. """Add an email address as a 3pid identifier
  512. Also adds an email pusher for the email address, if configured in the
  513. HS config
  514. Must be called on master.
  515. Args:
  516. user_id (str): id of user
  517. threepid (object): m.login.email.identity auth response
  518. token (str|None): access_token for the user, or None if not logged
  519. in.
  520. """
  521. reqd = ("medium", "address", "validated_at")
  522. if any(x not in threepid for x in reqd):
  523. # This will only happen if the ID server returns a malformed response
  524. logger.info("Can't add incomplete 3pid")
  525. return
  526. await self._auth_handler.add_threepid(
  527. user_id, threepid["medium"], threepid["address"], threepid["validated_at"],
  528. )
  529. # And we add an email pusher for them by default, but only
  530. # if email notifications are enabled (so people don't start
  531. # getting mail spam where they weren't before if email
  532. # notifs are set up on a homeserver)
  533. if (
  534. self.hs.config.email_enable_notifs
  535. and self.hs.config.email_notif_for_new_users
  536. and token
  537. ):
  538. # Pull the ID of the access token back out of the db
  539. # It would really make more sense for this to be passed
  540. # up when the access token is saved, but that's quite an
  541. # invasive change I'd rather do separately.
  542. user_tuple = await self.store.get_user_by_access_token(token)
  543. token_id = user_tuple["token_id"]
  544. await self.pusher_pool.add_pusher(
  545. user_id=user_id,
  546. access_token=token_id,
  547. kind="email",
  548. app_id="m.email",
  549. app_display_name="Email Notifications",
  550. device_display_name=threepid["address"],
  551. pushkey=threepid["address"],
  552. lang=None, # We don't know a user's language here
  553. data={},
  554. )
  555. async def _register_msisdn_threepid(self, user_id, threepid):
  556. """Add a phone number as a 3pid identifier
  557. Must be called on master.
  558. Args:
  559. user_id (str): id of user
  560. threepid (object): m.login.msisdn auth response
  561. """
  562. try:
  563. assert_params_in_dict(threepid, ["medium", "address", "validated_at"])
  564. except SynapseError as ex:
  565. if ex.errcode == Codes.MISSING_PARAM:
  566. # This will only happen if the ID server returns a malformed response
  567. logger.info("Can't add incomplete 3pid")
  568. return None
  569. raise
  570. await self._auth_handler.add_threepid(
  571. user_id, threepid["medium"], threepid["address"], threepid["validated_at"],
  572. )