register.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  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 twisted.internet import defer
  18. from synapse import types
  19. from synapse.api.constants import MAX_USERID_LENGTH, LoginType
  20. from synapse.api.errors import (
  21. AuthError,
  22. Codes,
  23. ConsentNotGivenError,
  24. InvalidCaptchaError,
  25. LimitExceededError,
  26. RegistrationError,
  27. SynapseError,
  28. )
  29. from synapse.config.server import is_threepid_reserved
  30. from synapse.http.client import CaptchaServerHttpClient
  31. from synapse.http.servlet import assert_params_in_dict
  32. from synapse.replication.http.login import RegisterDeviceReplicationServlet
  33. from synapse.replication.http.register import (
  34. ReplicationPostRegisterActionsServlet,
  35. ReplicationRegisterServlet,
  36. )
  37. from synapse.types import RoomAlias, RoomID, UserID, create_requester
  38. from synapse.util.async_helpers import Linearizer
  39. from synapse.util.threepids import check_3pid_allowed
  40. from ._base import BaseHandler
  41. logger = logging.getLogger(__name__)
  42. class RegistrationHandler(BaseHandler):
  43. def __init__(self, hs):
  44. """
  45. Args:
  46. hs (synapse.server.HomeServer):
  47. """
  48. super(RegistrationHandler, self).__init__(hs)
  49. self.hs = hs
  50. self.auth = hs.get_auth()
  51. self._auth_handler = hs.get_auth_handler()
  52. self.profile_handler = hs.get_profile_handler()
  53. self.user_directory_handler = hs.get_user_directory_handler()
  54. self.captcha_client = CaptchaServerHttpClient(hs)
  55. self.identity_handler = self.hs.get_handlers().identity_handler
  56. self.ratelimiter = hs.get_registration_ratelimiter()
  57. self._next_generated_user_id = None
  58. self.macaroon_gen = hs.get_macaroon_generator()
  59. self._generate_user_id_linearizer = Linearizer(
  60. name="_generate_user_id_linearizer"
  61. )
  62. self._server_notices_mxid = hs.config.server_notices_mxid
  63. if hs.config.worker_app:
  64. self._register_client = ReplicationRegisterServlet.make_client(hs)
  65. self._register_device_client = RegisterDeviceReplicationServlet.make_client(
  66. hs
  67. )
  68. self._post_registration_client = ReplicationPostRegisterActionsServlet.make_client(
  69. hs
  70. )
  71. else:
  72. self.device_handler = hs.get_device_handler()
  73. self.pusher_pool = hs.get_pusherpool()
  74. self.session_lifetime = hs.config.session_lifetime
  75. @defer.inlineCallbacks
  76. def check_username(self, localpart, guest_access_token=None, assigned_user_id=None):
  77. if types.contains_invalid_mxid_characters(localpart):
  78. raise SynapseError(
  79. 400,
  80. "User ID can only contain characters a-z, 0-9, or '=_-./'",
  81. Codes.INVALID_USERNAME,
  82. )
  83. if not localpart:
  84. raise SynapseError(400, "User ID cannot be empty", Codes.INVALID_USERNAME)
  85. if localpart[0] == "_":
  86. raise SynapseError(
  87. 400, "User ID may not begin with _", Codes.INVALID_USERNAME
  88. )
  89. user = UserID(localpart, self.hs.hostname)
  90. user_id = user.to_string()
  91. if assigned_user_id:
  92. if user_id == assigned_user_id:
  93. return
  94. else:
  95. raise SynapseError(
  96. 400,
  97. "A different user ID has already been registered for this session",
  98. )
  99. self.check_user_id_not_appservice_exclusive(user_id)
  100. if len(user_id) > MAX_USERID_LENGTH:
  101. raise SynapseError(
  102. 400,
  103. "User ID may not be longer than %s characters" % (MAX_USERID_LENGTH,),
  104. Codes.INVALID_USERNAME,
  105. )
  106. users = yield self.store.get_users_by_id_case_insensitive(user_id)
  107. if users:
  108. if not guest_access_token:
  109. raise SynapseError(
  110. 400, "User ID already taken.", errcode=Codes.USER_IN_USE
  111. )
  112. user_data = yield self.auth.get_user_by_access_token(guest_access_token)
  113. if not user_data["is_guest"] or user_data["user"].localpart != localpart:
  114. raise AuthError(
  115. 403,
  116. "Cannot register taken user ID without valid guest "
  117. "credentials for that user.",
  118. errcode=Codes.FORBIDDEN,
  119. )
  120. @defer.inlineCallbacks
  121. def register_user(
  122. self,
  123. localpart=None,
  124. password=None,
  125. guest_access_token=None,
  126. make_guest=False,
  127. admin=False,
  128. threepid=None,
  129. user_type=None,
  130. default_display_name=None,
  131. address=None,
  132. bind_emails=[],
  133. ):
  134. """Registers a new client on the server.
  135. Args:
  136. localpart : The local part of the user ID to register. If None,
  137. one will be generated.
  138. password (unicode) : The password to assign to this user so they can
  139. login again. This can be None which means they cannot login again
  140. via a password (e.g. the user is an application service user).
  141. user_type (str|None): type of user. One of the values from
  142. api.constants.UserTypes, or None for a normal user.
  143. default_display_name (unicode|None): if set, the new user's displayname
  144. will be set to this. Defaults to 'localpart'.
  145. address (str|None): the IP address used to perform the registration.
  146. bind_emails (List[str]): list of emails to bind to this account.
  147. Returns:
  148. Deferred[str]: user_id
  149. Raises:
  150. RegistrationError if there was a problem registering.
  151. """
  152. yield self.auth.check_auth_blocking(threepid=threepid)
  153. password_hash = None
  154. if password:
  155. password_hash = yield self._auth_handler.hash(password)
  156. if localpart:
  157. yield self.check_username(localpart, guest_access_token=guest_access_token)
  158. was_guest = guest_access_token is not None
  159. if not was_guest:
  160. try:
  161. int(localpart)
  162. raise RegistrationError(
  163. 400, "Numeric user IDs are reserved for guest users."
  164. )
  165. except ValueError:
  166. pass
  167. user = UserID(localpart, self.hs.hostname)
  168. user_id = user.to_string()
  169. if was_guest:
  170. # If the user was a guest then they already have a profile
  171. default_display_name = None
  172. elif default_display_name is None:
  173. default_display_name = localpart
  174. yield self.register_with_store(
  175. user_id=user_id,
  176. password_hash=password_hash,
  177. was_guest=was_guest,
  178. make_guest=make_guest,
  179. create_profile_with_displayname=default_display_name,
  180. admin=admin,
  181. user_type=user_type,
  182. address=address,
  183. )
  184. if self.hs.config.user_directory_search_all_users:
  185. profile = yield self.store.get_profileinfo(localpart)
  186. yield self.user_directory_handler.handle_local_profile_change(
  187. user_id, profile
  188. )
  189. else:
  190. # autogen a sequential user ID
  191. attempts = 0
  192. user = None
  193. while not user:
  194. localpart = yield self._generate_user_id(attempts > 0)
  195. user = UserID(localpart, self.hs.hostname)
  196. user_id = user.to_string()
  197. yield self.check_user_id_not_appservice_exclusive(user_id)
  198. if default_display_name is None:
  199. default_display_name = localpart
  200. try:
  201. yield self.register_with_store(
  202. user_id=user_id,
  203. password_hash=password_hash,
  204. make_guest=make_guest,
  205. create_profile_with_displayname=default_display_name,
  206. address=address,
  207. )
  208. except SynapseError:
  209. # if user id is taken, just generate another
  210. user = None
  211. user_id = None
  212. attempts += 1
  213. if not self.hs.config.user_consent_at_registration:
  214. yield self._auto_join_rooms(user_id)
  215. else:
  216. logger.info(
  217. "Skipping auto-join for %s because consent is required at registration",
  218. user_id,
  219. )
  220. # Bind any specified emails to this account
  221. current_time = self.hs.get_clock().time_msec()
  222. for email in bind_emails:
  223. # generate threepid dict
  224. threepid_dict = {
  225. "medium": "email",
  226. "address": email,
  227. "validated_at": current_time,
  228. }
  229. # Bind email to new account
  230. yield self._register_email_threepid(user_id, threepid_dict, None, False)
  231. return user_id
  232. @defer.inlineCallbacks
  233. def _auto_join_rooms(self, user_id):
  234. """Automatically joins users to auto join rooms - creating the room in the first place
  235. if the user is the first to be created.
  236. Args:
  237. user_id(str): The user to join
  238. """
  239. # auto-join the user to any rooms we're supposed to dump them into
  240. fake_requester = create_requester(user_id)
  241. # try to create the room if we're the first real user on the server. Note
  242. # that an auto-generated support user is not a real user and will never be
  243. # the user to create the room
  244. should_auto_create_rooms = False
  245. is_support = yield self.store.is_support_user(user_id)
  246. # There is an edge case where the first user is the support user, then
  247. # the room is never created, though this seems unlikely and
  248. # recoverable from given the support user being involved in the first
  249. # place.
  250. if self.hs.config.autocreate_auto_join_rooms and not is_support:
  251. count = yield self.store.count_all_users()
  252. should_auto_create_rooms = count == 1
  253. for r in self.hs.config.auto_join_rooms:
  254. logger.info("Auto-joining %s to %s", user_id, r)
  255. try:
  256. if should_auto_create_rooms:
  257. room_alias = RoomAlias.from_string(r)
  258. if self.hs.hostname != room_alias.domain:
  259. logger.warning(
  260. "Cannot create room alias %s, "
  261. "it does not match server domain",
  262. r,
  263. )
  264. else:
  265. # create room expects the localpart of the room alias
  266. room_alias_localpart = room_alias.localpart
  267. # getting the RoomCreationHandler during init gives a dependency
  268. # loop
  269. yield self.hs.get_room_creation_handler().create_room(
  270. fake_requester,
  271. config={
  272. "preset": "public_chat",
  273. "room_alias_name": room_alias_localpart,
  274. },
  275. ratelimit=False,
  276. )
  277. else:
  278. yield self._join_user_to_room(fake_requester, r)
  279. except ConsentNotGivenError as e:
  280. # Technically not necessary to pull out this error though
  281. # moving away from bare excepts is a good thing to do.
  282. logger.error("Failed to join new user to %r: %r", r, e)
  283. except Exception as e:
  284. logger.error("Failed to join new user to %r: %r", r, e)
  285. @defer.inlineCallbacks
  286. def post_consent_actions(self, user_id):
  287. """A series of registration actions that can only be carried out once consent
  288. has been granted
  289. Args:
  290. user_id (str): The user to join
  291. """
  292. yield self._auto_join_rooms(user_id)
  293. @defer.inlineCallbacks
  294. def appservice_register(self, user_localpart, as_token):
  295. user = UserID(user_localpart, self.hs.hostname)
  296. user_id = user.to_string()
  297. service = self.store.get_app_service_by_token(as_token)
  298. if not service:
  299. raise AuthError(403, "Invalid application service token.")
  300. if not service.is_interested_in_user(user_id):
  301. raise SynapseError(
  302. 400,
  303. "Invalid user localpart for this application service.",
  304. errcode=Codes.EXCLUSIVE,
  305. )
  306. service_id = service.id if service.is_exclusive_user(user_id) else None
  307. yield self.check_user_id_not_appservice_exclusive(
  308. user_id, allowed_appservice=service
  309. )
  310. yield self.register_with_store(
  311. user_id=user_id,
  312. password_hash="",
  313. appservice_id=service_id,
  314. create_profile_with_displayname=user.localpart,
  315. )
  316. return user_id
  317. @defer.inlineCallbacks
  318. def check_recaptcha(self, ip, private_key, challenge, response):
  319. """
  320. Checks a recaptcha is correct.
  321. Used only by c/s api v1
  322. """
  323. captcha_response = yield self._validate_captcha(
  324. ip, private_key, challenge, response
  325. )
  326. if not captcha_response["valid"]:
  327. logger.info(
  328. "Invalid captcha entered from %s. Error: %s",
  329. ip,
  330. captcha_response["error_url"],
  331. )
  332. raise InvalidCaptchaError(error_url=captcha_response["error_url"])
  333. else:
  334. logger.info("Valid captcha entered from %s", ip)
  335. @defer.inlineCallbacks
  336. def register_email(self, threepidCreds):
  337. """
  338. Registers emails with an identity server.
  339. Used only by c/s api v1
  340. """
  341. for c in threepidCreds:
  342. logger.info(
  343. "validating threepidcred sid %s on id server %s",
  344. c["sid"],
  345. c["idServer"],
  346. )
  347. try:
  348. threepid = yield self.identity_handler.threepid_from_creds(c)
  349. except Exception:
  350. logger.exception("Couldn't validate 3pid")
  351. raise RegistrationError(400, "Couldn't validate 3pid")
  352. if not threepid:
  353. raise RegistrationError(400, "Couldn't validate 3pid")
  354. logger.info(
  355. "got threepid with medium '%s' and address '%s'",
  356. threepid["medium"],
  357. threepid["address"],
  358. )
  359. if not check_3pid_allowed(self.hs, threepid["medium"], threepid["address"]):
  360. raise RegistrationError(403, "Third party identifier is not allowed")
  361. @defer.inlineCallbacks
  362. def bind_emails(self, user_id, threepidCreds):
  363. """Links emails with a user ID and informs an identity server.
  364. Used only by c/s api v1
  365. """
  366. # Now we have a matrix ID, bind it to the threepids we were given
  367. for c in threepidCreds:
  368. # XXX: This should be a deferred list, shouldn't it?
  369. yield self.identity_handler.bind_threepid(c, user_id)
  370. def check_user_id_not_appservice_exclusive(self, user_id, allowed_appservice=None):
  371. # don't allow people to register the server notices mxid
  372. if self._server_notices_mxid is not None:
  373. if user_id == self._server_notices_mxid:
  374. raise SynapseError(
  375. 400, "This user ID is reserved.", errcode=Codes.EXCLUSIVE
  376. )
  377. # valid user IDs must not clash with any user ID namespaces claimed by
  378. # application services.
  379. services = self.store.get_app_services()
  380. interested_services = [
  381. s
  382. for s in services
  383. if s.is_interested_in_user(user_id) and s != allowed_appservice
  384. ]
  385. for service in interested_services:
  386. if service.is_exclusive_user(user_id):
  387. raise SynapseError(
  388. 400,
  389. "This user ID is reserved by an application service.",
  390. errcode=Codes.EXCLUSIVE,
  391. )
  392. @defer.inlineCallbacks
  393. def _generate_user_id(self, reseed=False):
  394. if reseed or self._next_generated_user_id is None:
  395. with (yield self._generate_user_id_linearizer.queue(())):
  396. if reseed or self._next_generated_user_id is None:
  397. self._next_generated_user_id = (
  398. yield self.store.find_next_generated_user_id_localpart()
  399. )
  400. id = self._next_generated_user_id
  401. self._next_generated_user_id += 1
  402. return str(id)
  403. @defer.inlineCallbacks
  404. def _validate_captcha(self, ip_addr, private_key, challenge, response):
  405. """Validates the captcha provided.
  406. Used only by c/s api v1
  407. Returns:
  408. dict: Containing 'valid'(bool) and 'error_url'(str) if invalid.
  409. """
  410. response = yield self._submit_captcha(ip_addr, private_key, challenge, response)
  411. # parse Google's response. Lovely format..
  412. lines = response.split("\n")
  413. json = {
  414. "valid": lines[0] == "true",
  415. "error_url": "http://www.recaptcha.net/recaptcha/api/challenge?"
  416. + "error=%s" % lines[1],
  417. }
  418. return json
  419. @defer.inlineCallbacks
  420. def _submit_captcha(self, ip_addr, private_key, challenge, response):
  421. """
  422. Used only by c/s api v1
  423. """
  424. data = yield self.captcha_client.post_urlencoded_get_raw(
  425. "http://www.recaptcha.net:80/recaptcha/api/verify",
  426. args={
  427. "privatekey": private_key,
  428. "remoteip": ip_addr,
  429. "challenge": challenge,
  430. "response": response,
  431. },
  432. )
  433. return data
  434. @defer.inlineCallbacks
  435. def _join_user_to_room(self, requester, room_identifier):
  436. room_id = None
  437. room_member_handler = self.hs.get_room_member_handler()
  438. if RoomID.is_valid(room_identifier):
  439. room_id = room_identifier
  440. elif RoomAlias.is_valid(room_identifier):
  441. room_alias = RoomAlias.from_string(room_identifier)
  442. room_id, remote_room_hosts = (
  443. yield room_member_handler.lookup_room_alias(room_alias)
  444. )
  445. room_id = room_id.to_string()
  446. else:
  447. raise SynapseError(
  448. 400, "%s was not legal room ID or room alias" % (room_identifier,)
  449. )
  450. yield room_member_handler.update_membership(
  451. requester=requester,
  452. target=requester.user,
  453. room_id=room_id,
  454. remote_room_hosts=remote_room_hosts,
  455. action="join",
  456. ratelimit=False,
  457. )
  458. def register_with_store(
  459. self,
  460. user_id,
  461. password_hash=None,
  462. was_guest=False,
  463. make_guest=False,
  464. appservice_id=None,
  465. create_profile_with_displayname=None,
  466. admin=False,
  467. user_type=None,
  468. address=None,
  469. ):
  470. """Register user in the datastore.
  471. Args:
  472. user_id (str): The desired user ID to register.
  473. password_hash (str|None): Optional. The password hash for this user.
  474. was_guest (bool): Optional. Whether this is a guest account being
  475. upgraded to a non-guest account.
  476. make_guest (boolean): True if the the new user should be guest,
  477. false to add a regular user account.
  478. appservice_id (str|None): The ID of the appservice registering the user.
  479. create_profile_with_displayname (unicode|None): Optionally create a
  480. profile for the user, setting their displayname to the given value
  481. admin (boolean): is an admin user?
  482. user_type (str|None): type of user. One of the values from
  483. api.constants.UserTypes, or None for a normal user.
  484. address (str|None): the IP address used to perform the registration.
  485. Returns:
  486. Deferred
  487. """
  488. # Don't rate limit for app services
  489. if appservice_id is None and address is not None:
  490. time_now = self.clock.time()
  491. allowed, time_allowed = self.ratelimiter.can_do_action(
  492. address,
  493. time_now_s=time_now,
  494. rate_hz=self.hs.config.rc_registration.per_second,
  495. burst_count=self.hs.config.rc_registration.burst_count,
  496. )
  497. if not allowed:
  498. raise LimitExceededError(
  499. retry_after_ms=int(1000 * (time_allowed - time_now))
  500. )
  501. if self.hs.config.worker_app:
  502. return self._register_client(
  503. user_id=user_id,
  504. password_hash=password_hash,
  505. was_guest=was_guest,
  506. make_guest=make_guest,
  507. appservice_id=appservice_id,
  508. create_profile_with_displayname=create_profile_with_displayname,
  509. admin=admin,
  510. user_type=user_type,
  511. address=address,
  512. )
  513. else:
  514. return self.store.register_user(
  515. user_id=user_id,
  516. password_hash=password_hash,
  517. was_guest=was_guest,
  518. make_guest=make_guest,
  519. appservice_id=appservice_id,
  520. create_profile_with_displayname=create_profile_with_displayname,
  521. admin=admin,
  522. user_type=user_type,
  523. )
  524. @defer.inlineCallbacks
  525. def register_device(self, user_id, device_id, initial_display_name, is_guest=False):
  526. """Register a device for a user and generate an access token.
  527. The access token will be limited by the homeserver's session_lifetime config.
  528. Args:
  529. user_id (str): full canonical @user:id
  530. device_id (str|None): The device ID to check, or None to generate
  531. a new one.
  532. initial_display_name (str|None): An optional display name for the
  533. device.
  534. is_guest (bool): Whether this is a guest account
  535. Returns:
  536. defer.Deferred[tuple[str, str]]: Tuple of device ID and access token
  537. """
  538. if self.hs.config.worker_app:
  539. r = yield self._register_device_client(
  540. user_id=user_id,
  541. device_id=device_id,
  542. initial_display_name=initial_display_name,
  543. is_guest=is_guest,
  544. )
  545. return (r["device_id"], r["access_token"])
  546. valid_until_ms = None
  547. if self.session_lifetime is not None:
  548. if is_guest:
  549. raise Exception(
  550. "session_lifetime is not currently implemented for guest access"
  551. )
  552. valid_until_ms = self.clock.time_msec() + self.session_lifetime
  553. device_id = yield self.device_handler.check_device_registered(
  554. user_id, device_id, initial_display_name
  555. )
  556. if is_guest:
  557. assert valid_until_ms is None
  558. access_token = self.macaroon_gen.generate_access_token(
  559. user_id, ["guest = true"]
  560. )
  561. else:
  562. access_token = yield self._auth_handler.get_access_token_for_user_id(
  563. user_id, device_id=device_id, valid_until_ms=valid_until_ms
  564. )
  565. return (device_id, access_token)
  566. @defer.inlineCallbacks
  567. def post_registration_actions(
  568. self, user_id, auth_result, access_token, bind_email, bind_msisdn
  569. ):
  570. """A user has completed registration
  571. Args:
  572. user_id (str): The user ID that consented
  573. auth_result (dict): The authenticated credentials of the newly
  574. registered user.
  575. access_token (str|None): The access token of the newly logged in
  576. device, or None if `inhibit_login` enabled.
  577. bind_email (bool): Whether to bind the email with the identity
  578. server.
  579. bind_msisdn (bool): Whether to bind the msisdn with the identity
  580. server.
  581. """
  582. if self.hs.config.worker_app:
  583. yield self._post_registration_client(
  584. user_id=user_id,
  585. auth_result=auth_result,
  586. access_token=access_token,
  587. bind_email=bind_email,
  588. bind_msisdn=bind_msisdn,
  589. )
  590. return
  591. if auth_result and LoginType.EMAIL_IDENTITY in auth_result:
  592. threepid = auth_result[LoginType.EMAIL_IDENTITY]
  593. # Necessary due to auth checks prior to the threepid being
  594. # written to the db
  595. if is_threepid_reserved(
  596. self.hs.config.mau_limits_reserved_threepids, threepid
  597. ):
  598. yield self.store.upsert_monthly_active_user(user_id)
  599. yield self._register_email_threepid(
  600. user_id, threepid, access_token, bind_email
  601. )
  602. if auth_result and LoginType.MSISDN in auth_result:
  603. threepid = auth_result[LoginType.MSISDN]
  604. yield self._register_msisdn_threepid(user_id, threepid, bind_msisdn)
  605. if auth_result and LoginType.TERMS in auth_result:
  606. yield self._on_user_consented(user_id, self.hs.config.user_consent_version)
  607. @defer.inlineCallbacks
  608. def _on_user_consented(self, user_id, consent_version):
  609. """A user consented to the terms on registration
  610. Args:
  611. user_id (str): The user ID that consented.
  612. consent_version (str): version of the policy the user has
  613. consented to.
  614. """
  615. logger.info("%s has consented to the privacy policy", user_id)
  616. yield self.store.user_set_consent_version(user_id, consent_version)
  617. yield self.post_consent_actions(user_id)
  618. @defer.inlineCallbacks
  619. def _register_email_threepid(self, user_id, threepid, token, bind_email):
  620. """Add an email address as a 3pid identifier
  621. Also adds an email pusher for the email address, if configured in the
  622. HS config
  623. Also optionally binds emails to the given user_id on the identity server
  624. Must be called on master.
  625. Args:
  626. user_id (str): id of user
  627. threepid (object): m.login.email.identity auth response
  628. token (str|None): access_token for the user, or None if not logged
  629. in.
  630. bind_email (bool): true if the client requested the email to be
  631. bound at the identity server
  632. Returns:
  633. defer.Deferred:
  634. """
  635. reqd = ("medium", "address", "validated_at")
  636. if any(x not in threepid for x in reqd):
  637. # This will only happen if the ID server returns a malformed response
  638. logger.info("Can't add incomplete 3pid")
  639. return
  640. yield self._auth_handler.add_threepid(
  641. user_id, threepid["medium"], threepid["address"], threepid["validated_at"]
  642. )
  643. # And we add an email pusher for them by default, but only
  644. # if email notifications are enabled (so people don't start
  645. # getting mail spam where they weren't before if email
  646. # notifs are set up on a home server)
  647. if (
  648. self.hs.config.email_enable_notifs
  649. and self.hs.config.email_notif_for_new_users
  650. and token
  651. ):
  652. # Pull the ID of the access token back out of the db
  653. # It would really make more sense for this to be passed
  654. # up when the access token is saved, but that's quite an
  655. # invasive change I'd rather do separately.
  656. user_tuple = yield self.store.get_user_by_access_token(token)
  657. token_id = user_tuple["token_id"]
  658. yield self.pusher_pool.add_pusher(
  659. user_id=user_id,
  660. access_token=token_id,
  661. kind="email",
  662. app_id="m.email",
  663. app_display_name="Email Notifications",
  664. device_display_name=threepid["address"],
  665. pushkey=threepid["address"],
  666. lang=None, # We don't know a user's language here
  667. data={},
  668. )
  669. if bind_email:
  670. logger.info("bind_email specified: binding")
  671. logger.debug("Binding emails %s to %s" % (threepid, user_id))
  672. yield self.identity_handler.bind_threepid(
  673. threepid["threepid_creds"], user_id
  674. )
  675. else:
  676. logger.info("bind_email not specified: not binding email")
  677. @defer.inlineCallbacks
  678. def _register_msisdn_threepid(self, user_id, threepid, bind_msisdn):
  679. """Add a phone number as a 3pid identifier
  680. Also optionally binds msisdn to the given user_id on the identity server
  681. Must be called on master.
  682. Args:
  683. user_id (str): id of user
  684. threepid (object): m.login.msisdn auth response
  685. token (str): access_token for the user
  686. bind_email (bool): true if the client requested the email to be
  687. bound at the identity server
  688. Returns:
  689. defer.Deferred:
  690. """
  691. try:
  692. assert_params_in_dict(threepid, ["medium", "address", "validated_at"])
  693. except SynapseError as ex:
  694. if ex.errcode == Codes.MISSING_PARAM:
  695. # This will only happen if the ID server returns a malformed response
  696. logger.info("Can't add incomplete 3pid")
  697. return None
  698. raise
  699. yield self._auth_handler.add_threepid(
  700. user_id, threepid["medium"], threepid["address"], threepid["validated_at"]
  701. )
  702. if bind_msisdn:
  703. logger.info("bind_msisdn specified: binding")
  704. logger.debug("Binding msisdn %s to %s", threepid, user_id)
  705. yield self.identity_handler.bind_threepid(
  706. threepid["threepid_creds"], user_id
  707. )
  708. else:
  709. logger.info("bind_msisdn not specified: not binding msisdn")