register.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015 - 2016 OpenMarket Ltd
  3. # Copyright 2017 Vector Creations Ltd
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import hmac
  17. import logging
  18. from typing import List, Union
  19. import synapse
  20. import synapse.api.auth
  21. import synapse.types
  22. from synapse.api.constants import LoginType
  23. from synapse.api.errors import (
  24. Codes,
  25. SynapseError,
  26. ThreepidValidationError,
  27. UnrecognizedRequestError,
  28. )
  29. from synapse.config import ConfigError
  30. from synapse.config.captcha import CaptchaConfig
  31. from synapse.config.consent_config import ConsentConfig
  32. from synapse.config.emailconfig import ThreepidBehaviour
  33. from synapse.config.ratelimiting import FederationRateLimitConfig
  34. from synapse.config.registration import RegistrationConfig
  35. from synapse.config.server import is_threepid_reserved
  36. from synapse.handlers.auth import AuthHandler
  37. from synapse.http.server import finish_request
  38. from synapse.http.servlet import (
  39. RestServlet,
  40. assert_params_in_dict,
  41. parse_json_object_from_request,
  42. parse_string,
  43. )
  44. from synapse.push.mailer import load_jinja2_templates
  45. from synapse.util.msisdn import phone_number_to_msisdn
  46. from synapse.util.ratelimitutils import FederationRateLimiter
  47. from synapse.util.stringutils import assert_valid_client_secret, random_string
  48. from synapse.util.threepids import check_3pid_allowed
  49. from ._base import client_patterns, interactive_auth_handler
  50. # We ought to be using hmac.compare_digest() but on older pythons it doesn't
  51. # exist. It's a _really minor_ security flaw to use plain string comparison
  52. # because the timing attack is so obscured by all the other code here it's
  53. # unlikely to make much difference
  54. if hasattr(hmac, "compare_digest"):
  55. compare_digest = hmac.compare_digest
  56. else:
  57. def compare_digest(a, b):
  58. return a == b
  59. logger = logging.getLogger(__name__)
  60. class EmailRegisterRequestTokenRestServlet(RestServlet):
  61. PATTERNS = client_patterns("/register/email/requestToken$")
  62. def __init__(self, hs):
  63. """
  64. Args:
  65. hs (synapse.server.HomeServer): server
  66. """
  67. super(EmailRegisterRequestTokenRestServlet, self).__init__()
  68. self.hs = hs
  69. self.identity_handler = hs.get_handlers().identity_handler
  70. self.config = hs.config
  71. if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
  72. from synapse.push.mailer import Mailer, load_jinja2_templates
  73. template_html, template_text = load_jinja2_templates(
  74. self.config.email_template_dir,
  75. [
  76. self.config.email_registration_template_html,
  77. self.config.email_registration_template_text,
  78. ],
  79. apply_format_ts_filter=True,
  80. apply_mxc_to_http_filter=True,
  81. public_baseurl=self.config.public_baseurl,
  82. )
  83. self.mailer = Mailer(
  84. hs=self.hs,
  85. app_name=self.config.email_app_name,
  86. template_html=template_html,
  87. template_text=template_text,
  88. )
  89. async def on_POST(self, request):
  90. if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.OFF:
  91. if self.hs.config.local_threepid_handling_disabled_due_to_email_config:
  92. logger.warning(
  93. "Email registration has been disabled due to lack of email config"
  94. )
  95. raise SynapseError(
  96. 400, "Email-based registration has been disabled on this server"
  97. )
  98. body = parse_json_object_from_request(request)
  99. assert_params_in_dict(body, ["client_secret", "email", "send_attempt"])
  100. # Extract params from body
  101. client_secret = body["client_secret"]
  102. assert_valid_client_secret(client_secret)
  103. email = body["email"]
  104. send_attempt = body["send_attempt"]
  105. next_link = body.get("next_link") # Optional param
  106. if not check_3pid_allowed(self.hs, "email", email):
  107. raise SynapseError(
  108. 403,
  109. "Your email domain is not authorized to register on this server",
  110. Codes.THREEPID_DENIED,
  111. )
  112. existing_user_id = await self.hs.get_datastore().get_user_id_by_threepid(
  113. "email", body["email"]
  114. )
  115. if existing_user_id is not None:
  116. if self.hs.config.request_token_inhibit_3pid_errors:
  117. # Make the client think the operation succeeded. See the rationale in the
  118. # comments for request_token_inhibit_3pid_errors.
  119. return 200, {"sid": random_string(16)}
  120. raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
  121. if self.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
  122. assert self.hs.config.account_threepid_delegate_email
  123. # Have the configured identity server handle the request
  124. ret = await self.identity_handler.requestEmailToken(
  125. self.hs.config.account_threepid_delegate_email,
  126. email,
  127. client_secret,
  128. send_attempt,
  129. next_link,
  130. )
  131. else:
  132. # Send registration emails from Synapse
  133. sid = await self.identity_handler.send_threepid_validation(
  134. email,
  135. client_secret,
  136. send_attempt,
  137. self.mailer.send_registration_mail,
  138. next_link,
  139. )
  140. # Wrap the session id in a JSON object
  141. ret = {"sid": sid}
  142. return 200, ret
  143. class MsisdnRegisterRequestTokenRestServlet(RestServlet):
  144. PATTERNS = client_patterns("/register/msisdn/requestToken$")
  145. def __init__(self, hs):
  146. """
  147. Args:
  148. hs (synapse.server.HomeServer): server
  149. """
  150. super(MsisdnRegisterRequestTokenRestServlet, self).__init__()
  151. self.hs = hs
  152. self.identity_handler = hs.get_handlers().identity_handler
  153. async def on_POST(self, request):
  154. body = parse_json_object_from_request(request)
  155. assert_params_in_dict(
  156. body, ["client_secret", "country", "phone_number", "send_attempt"]
  157. )
  158. client_secret = body["client_secret"]
  159. country = body["country"]
  160. phone_number = body["phone_number"]
  161. send_attempt = body["send_attempt"]
  162. next_link = body.get("next_link") # Optional param
  163. msisdn = phone_number_to_msisdn(country, phone_number)
  164. if not check_3pid_allowed(self.hs, "msisdn", msisdn):
  165. raise SynapseError(
  166. 403,
  167. "Phone numbers are not authorized to register on this server",
  168. Codes.THREEPID_DENIED,
  169. )
  170. existing_user_id = await self.hs.get_datastore().get_user_id_by_threepid(
  171. "msisdn", msisdn
  172. )
  173. if existing_user_id is not None:
  174. if self.hs.config.request_token_inhibit_3pid_errors:
  175. # Make the client think the operation succeeded. See the rationale in the
  176. # comments for request_token_inhibit_3pid_errors.
  177. return 200, {"sid": random_string(16)}
  178. raise SynapseError(
  179. 400, "Phone number is already in use", Codes.THREEPID_IN_USE
  180. )
  181. if not self.hs.config.account_threepid_delegate_msisdn:
  182. logger.warning(
  183. "No upstream msisdn account_threepid_delegate configured on the server to "
  184. "handle this request"
  185. )
  186. raise SynapseError(
  187. 400, "Registration by phone number is not supported on this homeserver"
  188. )
  189. ret = await self.identity_handler.requestMsisdnToken(
  190. self.hs.config.account_threepid_delegate_msisdn,
  191. country,
  192. phone_number,
  193. client_secret,
  194. send_attempt,
  195. next_link,
  196. )
  197. return 200, ret
  198. class RegistrationSubmitTokenServlet(RestServlet):
  199. """Handles registration 3PID validation token submission"""
  200. PATTERNS = client_patterns(
  201. "/registration/(?P<medium>[^/]*)/submit_token$", releases=(), unstable=True
  202. )
  203. def __init__(self, hs):
  204. """
  205. Args:
  206. hs (synapse.server.HomeServer): server
  207. """
  208. super(RegistrationSubmitTokenServlet, self).__init__()
  209. self.hs = hs
  210. self.auth = hs.get_auth()
  211. self.config = hs.config
  212. self.clock = hs.get_clock()
  213. self.store = hs.get_datastore()
  214. if self.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
  215. (self.failure_email_template,) = load_jinja2_templates(
  216. self.config.email_template_dir,
  217. [self.config.email_registration_template_failure_html],
  218. )
  219. if self.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
  220. (self.failure_email_template,) = load_jinja2_templates(
  221. self.config.email_template_dir,
  222. [self.config.email_registration_template_failure_html],
  223. )
  224. async def on_GET(self, request, medium):
  225. if medium != "email":
  226. raise SynapseError(
  227. 400, "This medium is currently not supported for registration"
  228. )
  229. if self.config.threepid_behaviour_email == ThreepidBehaviour.OFF:
  230. if self.config.local_threepid_handling_disabled_due_to_email_config:
  231. logger.warning(
  232. "User registration via email has been disabled due to lack of email config"
  233. )
  234. raise SynapseError(
  235. 400, "Email-based registration is disabled on this server"
  236. )
  237. sid = parse_string(request, "sid", required=True)
  238. client_secret = parse_string(request, "client_secret", required=True)
  239. token = parse_string(request, "token", required=True)
  240. # Attempt to validate a 3PID session
  241. try:
  242. # Mark the session as valid
  243. next_link = await self.store.validate_threepid_session(
  244. sid, client_secret, token, self.clock.time_msec()
  245. )
  246. # Perform a 302 redirect if next_link is set
  247. if next_link:
  248. if next_link.startswith("file:///"):
  249. logger.warning(
  250. "Not redirecting to next_link as it is a local file: address"
  251. )
  252. else:
  253. request.setResponseCode(302)
  254. request.setHeader("Location", next_link)
  255. finish_request(request)
  256. return None
  257. # Otherwise show the success template
  258. html = self.config.email_registration_template_success_html_content
  259. request.setResponseCode(200)
  260. except ThreepidValidationError as e:
  261. request.setResponseCode(e.code)
  262. # Show a failure page with a reason
  263. template_vars = {"failure_reason": e.msg}
  264. html = self.failure_email_template.render(**template_vars)
  265. request.write(html.encode("utf-8"))
  266. finish_request(request)
  267. class UsernameAvailabilityRestServlet(RestServlet):
  268. PATTERNS = client_patterns("/register/available")
  269. def __init__(self, hs):
  270. """
  271. Args:
  272. hs (synapse.server.HomeServer): server
  273. """
  274. super(UsernameAvailabilityRestServlet, self).__init__()
  275. self.hs = hs
  276. self.registration_handler = hs.get_registration_handler()
  277. self.ratelimiter = FederationRateLimiter(
  278. hs.get_clock(),
  279. FederationRateLimitConfig(
  280. # Time window of 2s
  281. window_size=2000,
  282. # Artificially delay requests if rate > sleep_limit/window_size
  283. sleep_limit=1,
  284. # Amount of artificial delay to apply
  285. sleep_msec=1000,
  286. # Error with 429 if more than reject_limit requests are queued
  287. reject_limit=1,
  288. # Allow 1 request at a time
  289. concurrent_requests=1,
  290. ),
  291. )
  292. async def on_GET(self, request):
  293. if not self.hs.config.enable_registration:
  294. raise SynapseError(
  295. 403, "Registration has been disabled", errcode=Codes.FORBIDDEN
  296. )
  297. ip = self.hs.get_ip_from_request(request)
  298. with self.ratelimiter.ratelimit(ip) as wait_deferred:
  299. await wait_deferred
  300. username = parse_string(request, "username", required=True)
  301. await self.registration_handler.check_username(username)
  302. return 200, {"available": True}
  303. class RegisterRestServlet(RestServlet):
  304. PATTERNS = client_patterns("/register$")
  305. def __init__(self, hs):
  306. """
  307. Args:
  308. hs (synapse.server.HomeServer): server
  309. """
  310. super(RegisterRestServlet, self).__init__()
  311. self.hs = hs
  312. self.auth = hs.get_auth()
  313. self.store = hs.get_datastore()
  314. self.auth_handler = hs.get_auth_handler()
  315. self.registration_handler = hs.get_registration_handler()
  316. self.identity_handler = hs.get_handlers().identity_handler
  317. self.room_member_handler = hs.get_room_member_handler()
  318. self.macaroon_gen = hs.get_macaroon_generator()
  319. self.ratelimiter = hs.get_registration_ratelimiter()
  320. self.password_policy_handler = hs.get_password_policy_handler()
  321. self.clock = hs.get_clock()
  322. self._registration_flows = _calculate_registration_flows(
  323. hs.config, self.auth_handler
  324. )
  325. @interactive_auth_handler
  326. async def on_POST(self, request):
  327. body = parse_json_object_from_request(request)
  328. client_addr = request.getClientIP()
  329. self.ratelimiter.ratelimit(client_addr, update=False)
  330. kind = b"user"
  331. if b"kind" in request.args:
  332. kind = request.args[b"kind"][0]
  333. if kind == b"guest":
  334. ret = await self._do_guest_registration(body, address=client_addr)
  335. return ret
  336. elif kind != b"user":
  337. raise UnrecognizedRequestError(
  338. "Do not understand membership kind: %s" % (kind.decode("utf8"),)
  339. )
  340. # we do basic sanity checks here because the auth layer will store these
  341. # in sessions. Pull out the username/password provided to us.
  342. if "password" in body:
  343. password = body.pop("password")
  344. if not isinstance(password, str) or len(password) > 512:
  345. raise SynapseError(400, "Invalid password")
  346. self.password_policy_handler.validate_password(password)
  347. # If the password is valid, hash it and store it back on the body.
  348. # This ensures that only the hashed password is handled everywhere.
  349. if "password_hash" in body:
  350. raise SynapseError(400, "Unexpected property: password_hash")
  351. body["password_hash"] = await self.auth_handler.hash(password)
  352. desired_username = None
  353. if "username" in body:
  354. if not isinstance(body["username"], str) or len(body["username"]) > 512:
  355. raise SynapseError(400, "Invalid username")
  356. desired_username = body["username"]
  357. appservice = None
  358. if self.auth.has_access_token(request):
  359. appservice = await self.auth.get_appservice_by_req(request)
  360. # fork off as soon as possible for ASes which have completely
  361. # different registration flows to normal users
  362. # == Application Service Registration ==
  363. if appservice:
  364. # Set the desired user according to the AS API (which uses the
  365. # 'user' key not 'username'). Since this is a new addition, we'll
  366. # fallback to 'username' if they gave one.
  367. desired_username = body.get("user", desired_username)
  368. # XXX we should check that desired_username is valid. Currently
  369. # we give appservices carte blanche for any insanity in mxids,
  370. # because the IRC bridges rely on being able to register stupid
  371. # IDs.
  372. access_token = self.auth.get_access_token_from_request(request)
  373. if isinstance(desired_username, str):
  374. result = await self._do_appservice_registration(
  375. desired_username, access_token, body
  376. )
  377. return 200, result # we throw for non 200 responses
  378. # for regular registration, downcase the provided username before
  379. # attempting to register it. This should mean
  380. # that people who try to register with upper-case in their usernames
  381. # don't get a nasty surprise. (Note that we treat username
  382. # case-insenstively in login, so they are free to carry on imagining
  383. # that their username is CrAzYh4cKeR if that keeps them happy)
  384. if desired_username is not None:
  385. desired_username = desired_username.lower()
  386. # == Normal User Registration == (everyone else)
  387. if not self.hs.config.enable_registration:
  388. raise SynapseError(403, "Registration has been disabled")
  389. guest_access_token = body.get("guest_access_token", None)
  390. if "initial_device_display_name" in body and "password_hash" not in body:
  391. # ignore 'initial_device_display_name' if sent without
  392. # a password to work around a client bug where it sent
  393. # the 'initial_device_display_name' param alone, wiping out
  394. # the original registration params
  395. logger.warning("Ignoring initial_device_display_name without password")
  396. del body["initial_device_display_name"]
  397. session_id = self.auth_handler.get_session_id(body)
  398. registered_user_id = None
  399. if session_id:
  400. # if we get a registered user id out of here, it means we previously
  401. # registered a user for this session, so we could just return the
  402. # user here. We carry on and go through the auth checks though,
  403. # for paranoia.
  404. registered_user_id = await self.auth_handler.get_session_data(
  405. session_id, "registered_user_id", None
  406. )
  407. if desired_username is not None:
  408. await self.registration_handler.check_username(
  409. desired_username,
  410. guest_access_token=guest_access_token,
  411. assigned_user_id=registered_user_id,
  412. )
  413. auth_result, params, session_id = await self.auth_handler.check_auth(
  414. self._registration_flows,
  415. request,
  416. body,
  417. self.hs.get_ip_from_request(request),
  418. "register a new account",
  419. )
  420. # Check that we're not trying to register a denied 3pid.
  421. #
  422. # the user-facing checks will probably already have happened in
  423. # /register/email/requestToken when we requested a 3pid, but that's not
  424. # guaranteed.
  425. if auth_result:
  426. for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]:
  427. if login_type in auth_result:
  428. medium = auth_result[login_type]["medium"]
  429. address = auth_result[login_type]["address"]
  430. if not check_3pid_allowed(self.hs, medium, address):
  431. raise SynapseError(
  432. 403,
  433. "Third party identifiers (email/phone numbers)"
  434. + " are not authorized on this server",
  435. Codes.THREEPID_DENIED,
  436. )
  437. if registered_user_id is not None:
  438. logger.info(
  439. "Already registered user ID %r for this session", registered_user_id
  440. )
  441. # don't re-register the threepids
  442. registered = False
  443. else:
  444. # NB: This may be from the auth handler and NOT from the POST
  445. assert_params_in_dict(params, ["password_hash"])
  446. desired_username = params.get("username", None)
  447. guest_access_token = params.get("guest_access_token", None)
  448. new_password_hash = params.get("password_hash", None)
  449. if desired_username is not None:
  450. desired_username = desired_username.lower()
  451. threepid = None
  452. if auth_result:
  453. threepid = auth_result.get(LoginType.EMAIL_IDENTITY)
  454. # Also check that we're not trying to register a 3pid that's already
  455. # been registered.
  456. #
  457. # This has probably happened in /register/email/requestToken as well,
  458. # but if a user hits this endpoint twice then clicks on each link from
  459. # the two activation emails, they would register the same 3pid twice.
  460. for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]:
  461. if login_type in auth_result:
  462. medium = auth_result[login_type]["medium"]
  463. address = auth_result[login_type]["address"]
  464. existing_user_id = await self.store.get_user_id_by_threepid(
  465. medium, address
  466. )
  467. if existing_user_id is not None:
  468. raise SynapseError(
  469. 400,
  470. "%s is already in use" % medium,
  471. Codes.THREEPID_IN_USE,
  472. )
  473. registered_user_id = await self.registration_handler.register_user(
  474. localpart=desired_username,
  475. password_hash=new_password_hash,
  476. guest_access_token=guest_access_token,
  477. threepid=threepid,
  478. address=client_addr,
  479. )
  480. # Necessary due to auth checks prior to the threepid being
  481. # written to the db
  482. if threepid:
  483. if is_threepid_reserved(
  484. self.hs.config.mau_limits_reserved_threepids, threepid
  485. ):
  486. await self.store.upsert_monthly_active_user(registered_user_id)
  487. # remember that we've now registered that user account, and with
  488. # what user ID (since the user may not have specified)
  489. await self.auth_handler.set_session_data(
  490. session_id, "registered_user_id", registered_user_id
  491. )
  492. registered = True
  493. return_dict = await self._create_registration_details(
  494. registered_user_id, params
  495. )
  496. if registered:
  497. await self.registration_handler.post_registration_actions(
  498. user_id=registered_user_id,
  499. auth_result=auth_result,
  500. access_token=return_dict.get("access_token"),
  501. )
  502. return 200, return_dict
  503. def on_OPTIONS(self, _):
  504. return 200, {}
  505. async def _do_appservice_registration(self, username, as_token, body):
  506. user_id = await self.registration_handler.appservice_register(
  507. username, as_token
  508. )
  509. return await self._create_registration_details(user_id, body)
  510. async def _create_registration_details(self, user_id, params):
  511. """Complete registration of newly-registered user
  512. Allocates device_id if one was not given; also creates access_token.
  513. Args:
  514. (str) user_id: full canonical @user:id
  515. (object) params: registration parameters, from which we pull
  516. device_id, initial_device_name and inhibit_login
  517. Returns:
  518. defer.Deferred: (object) dictionary for response from /register
  519. """
  520. result = {"user_id": user_id, "home_server": self.hs.hostname}
  521. if not params.get("inhibit_login", False):
  522. device_id = params.get("device_id")
  523. initial_display_name = params.get("initial_device_display_name")
  524. device_id, access_token = await self.registration_handler.register_device(
  525. user_id, device_id, initial_display_name, is_guest=False
  526. )
  527. result.update({"access_token": access_token, "device_id": device_id})
  528. return result
  529. async def _do_guest_registration(self, params, address=None):
  530. if not self.hs.config.allow_guest_access:
  531. raise SynapseError(403, "Guest access is disabled")
  532. user_id = await self.registration_handler.register_user(
  533. make_guest=True, address=address
  534. )
  535. # we don't allow guests to specify their own device_id, because
  536. # we have nowhere to store it.
  537. device_id = synapse.api.auth.GUEST_DEVICE_ID
  538. initial_display_name = params.get("initial_device_display_name")
  539. device_id, access_token = await self.registration_handler.register_device(
  540. user_id, device_id, initial_display_name, is_guest=True
  541. )
  542. return (
  543. 200,
  544. {
  545. "user_id": user_id,
  546. "device_id": device_id,
  547. "access_token": access_token,
  548. "home_server": self.hs.hostname,
  549. },
  550. )
  551. def _calculate_registration_flows(
  552. # technically `config` has to provide *all* of these interfaces, not just one
  553. config: Union[RegistrationConfig, ConsentConfig, CaptchaConfig],
  554. auth_handler: AuthHandler,
  555. ) -> List[List[str]]:
  556. """Get a suitable flows list for registration
  557. Args:
  558. config: server configuration
  559. auth_handler: authorization handler
  560. Returns: a list of supported flows
  561. """
  562. # FIXME: need a better error than "no auth flow found" for scenarios
  563. # where we required 3PID for registration but the user didn't give one
  564. require_email = "email" in config.registrations_require_3pid
  565. require_msisdn = "msisdn" in config.registrations_require_3pid
  566. show_msisdn = True
  567. show_email = True
  568. if config.disable_msisdn_registration:
  569. show_msisdn = False
  570. require_msisdn = False
  571. enabled_auth_types = auth_handler.get_enabled_auth_types()
  572. if LoginType.EMAIL_IDENTITY not in enabled_auth_types:
  573. show_email = False
  574. if require_email:
  575. raise ConfigError(
  576. "Configuration requires email address at registration, but email "
  577. "validation is not configured"
  578. )
  579. if LoginType.MSISDN not in enabled_auth_types:
  580. show_msisdn = False
  581. if require_msisdn:
  582. raise ConfigError(
  583. "Configuration requires msisdn at registration, but msisdn "
  584. "validation is not configured"
  585. )
  586. flows = []
  587. # only support 3PIDless registration if no 3PIDs are required
  588. if not require_email and not require_msisdn:
  589. # Add a dummy step here, otherwise if a client completes
  590. # recaptcha first we'll assume they were going for this flow
  591. # and complete the request, when they could have been trying to
  592. # complete one of the flows with email/msisdn auth.
  593. flows.append([LoginType.DUMMY])
  594. # only support the email-only flow if we don't require MSISDN 3PIDs
  595. if show_email and not require_msisdn:
  596. flows.append([LoginType.EMAIL_IDENTITY])
  597. # only support the MSISDN-only flow if we don't require email 3PIDs
  598. if show_msisdn and not require_email:
  599. flows.append([LoginType.MSISDN])
  600. if show_email and show_msisdn:
  601. # always let users provide both MSISDN & email
  602. flows.append([LoginType.MSISDN, LoginType.EMAIL_IDENTITY])
  603. # Prepend m.login.terms to all flows if we're requiring consent
  604. if config.user_consent_at_registration:
  605. for flow in flows:
  606. flow.insert(0, LoginType.TERMS)
  607. # Prepend recaptcha to all flows if we're requiring captcha
  608. if config.enable_registration_captcha:
  609. for flow in flows:
  610. flow.insert(0, LoginType.RECAPTCHA)
  611. return flows
  612. def register_servlets(hs, http_server):
  613. EmailRegisterRequestTokenRestServlet(hs).register(http_server)
  614. MsisdnRegisterRequestTokenRestServlet(hs).register(http_server)
  615. UsernameAvailabilityRestServlet(hs).register(http_server)
  616. RegistrationSubmitTokenServlet(hs).register(http_server)
  617. RegisterRestServlet(hs).register(http_server)