You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

963 lines
37 KiB

  1. # Copyright 2015 - 2016 OpenMarket Ltd
  2. # Copyright 2017 Vector Creations 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. import logging
  16. import random
  17. from typing import TYPE_CHECKING, List, Optional, Tuple
  18. from twisted.web.server import Request
  19. import synapse
  20. import synapse.api.auth
  21. import synapse.types
  22. from synapse.api.constants import (
  23. APP_SERVICE_REGISTRATION_TYPE,
  24. ApprovalNoticeMedium,
  25. LoginType,
  26. )
  27. from synapse.api.errors import (
  28. Codes,
  29. InteractiveAuthIncompleteError,
  30. NotApprovedError,
  31. SynapseError,
  32. ThreepidValidationError,
  33. UnrecognizedRequestError,
  34. )
  35. from synapse.api.ratelimiting import Ratelimiter
  36. from synapse.config import ConfigError
  37. from synapse.config.homeserver import HomeServerConfig
  38. from synapse.config.ratelimiting import FederationRatelimitSettings
  39. from synapse.config.server import is_threepid_reserved
  40. from synapse.handlers.auth import AuthHandler
  41. from synapse.handlers.ui_auth import UIAuthSessionDataConstants
  42. from synapse.http.server import HttpServer, finish_request, respond_with_html
  43. from synapse.http.servlet import (
  44. RestServlet,
  45. assert_params_in_dict,
  46. parse_json_object_from_request,
  47. parse_string,
  48. )
  49. from synapse.http.site import SynapseRequest
  50. from synapse.metrics import threepid_send_requests
  51. from synapse.push.mailer import Mailer
  52. from synapse.types import JsonDict
  53. from synapse.util.msisdn import phone_number_to_msisdn
  54. from synapse.util.ratelimitutils import FederationRateLimiter
  55. from synapse.util.stringutils import assert_valid_client_secret, random_string
  56. from synapse.util.threepids import (
  57. canonicalise_email,
  58. check_3pid_allowed,
  59. validate_email,
  60. )
  61. from ._base import client_patterns, interactive_auth_handler
  62. if TYPE_CHECKING:
  63. from synapse.server import HomeServer
  64. logger = logging.getLogger(__name__)
  65. class EmailRegisterRequestTokenRestServlet(RestServlet):
  66. PATTERNS = client_patterns("/register/email/requestToken$")
  67. def __init__(self, hs: "HomeServer"):
  68. super().__init__()
  69. self.hs = hs
  70. self.identity_handler = hs.get_identity_handler()
  71. self.config = hs.config
  72. if self.hs.config.email.can_verify_email:
  73. self.mailer = Mailer(
  74. hs=self.hs,
  75. app_name=self.config.email.email_app_name,
  76. template_html=self.config.email.email_registration_template_html,
  77. template_text=self.config.email.email_registration_template_text,
  78. )
  79. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  80. if not self.hs.config.email.can_verify_email:
  81. logger.warning(
  82. "Email registration has been disabled due to lack of email config"
  83. )
  84. raise SynapseError(
  85. 400, "Email-based registration has been disabled on this server"
  86. )
  87. body = parse_json_object_from_request(request)
  88. assert_params_in_dict(body, ["client_secret", "email", "send_attempt"])
  89. # Extract params from body
  90. client_secret = body["client_secret"]
  91. assert_valid_client_secret(client_secret)
  92. # For emails, canonicalise the address.
  93. # We store all email addresses canonicalised in the DB.
  94. # (See on_POST in EmailThreepidRequestTokenRestServlet
  95. # in synapse/rest/client/account.py)
  96. try:
  97. email = validate_email(body["email"])
  98. except ValueError as e:
  99. raise SynapseError(400, str(e))
  100. send_attempt = body["send_attempt"]
  101. next_link = body.get("next_link") # Optional param
  102. if not await check_3pid_allowed(self.hs, "email", email, registration=True):
  103. raise SynapseError(
  104. 403,
  105. "Your email domain is not authorized to register on this server",
  106. Codes.THREEPID_DENIED,
  107. )
  108. await self.identity_handler.ratelimit_request_token_requests(
  109. request, "email", email
  110. )
  111. existing_user_id = await self.hs.get_datastores().main.get_user_id_by_threepid(
  112. "email", email
  113. )
  114. if existing_user_id is not None:
  115. if self.hs.config.server.request_token_inhibit_3pid_errors:
  116. # Make the client think the operation succeeded. See the rationale in the
  117. # comments for request_token_inhibit_3pid_errors.
  118. # Also wait for some random amount of time between 100ms and 1s to make it
  119. # look like we did something.
  120. await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
  121. return 200, {"sid": random_string(16)}
  122. raise SynapseError(400, "Email is already in use", Codes.THREEPID_IN_USE)
  123. # Send registration emails from Synapse
  124. sid = await self.identity_handler.send_threepid_validation(
  125. email,
  126. client_secret,
  127. send_attempt,
  128. self.mailer.send_registration_mail,
  129. next_link,
  130. )
  131. threepid_send_requests.labels(type="email", reason="register").observe(
  132. send_attempt
  133. )
  134. # Wrap the session id in a JSON object
  135. return 200, {"sid": sid}
  136. class MsisdnRegisterRequestTokenRestServlet(RestServlet):
  137. PATTERNS = client_patterns("/register/msisdn/requestToken$")
  138. def __init__(self, hs: "HomeServer"):
  139. super().__init__()
  140. self.hs = hs
  141. self.identity_handler = hs.get_identity_handler()
  142. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  143. body = parse_json_object_from_request(request)
  144. assert_params_in_dict(
  145. body, ["client_secret", "country", "phone_number", "send_attempt"]
  146. )
  147. client_secret = body["client_secret"]
  148. assert_valid_client_secret(client_secret)
  149. country = body["country"]
  150. phone_number = body["phone_number"]
  151. send_attempt = body["send_attempt"]
  152. next_link = body.get("next_link") # Optional param
  153. msisdn = phone_number_to_msisdn(country, phone_number)
  154. if not await check_3pid_allowed(self.hs, "msisdn", msisdn, registration=True):
  155. raise SynapseError(
  156. 403,
  157. "Phone numbers are not authorized to register on this server",
  158. Codes.THREEPID_DENIED,
  159. )
  160. await self.identity_handler.ratelimit_request_token_requests(
  161. request, "msisdn", msisdn
  162. )
  163. existing_user_id = await self.hs.get_datastores().main.get_user_id_by_threepid(
  164. "msisdn", msisdn
  165. )
  166. if existing_user_id is not None:
  167. if self.hs.config.server.request_token_inhibit_3pid_errors:
  168. # Make the client think the operation succeeded. See the rationale in the
  169. # comments for request_token_inhibit_3pid_errors.
  170. # Also wait for some random amount of time between 100ms and 1s to make it
  171. # look like we did something.
  172. await self.hs.get_clock().sleep(random.randint(1, 10) / 10)
  173. return 200, {"sid": random_string(16)}
  174. raise SynapseError(
  175. 400, "Phone number is already in use", Codes.THREEPID_IN_USE
  176. )
  177. if not self.hs.config.registration.account_threepid_delegate_msisdn:
  178. logger.warning(
  179. "No upstream msisdn account_threepid_delegate configured on the server to "
  180. "handle this request"
  181. )
  182. raise SynapseError(
  183. 400, "Registration by phone number is not supported on this homeserver"
  184. )
  185. ret = await self.identity_handler.requestMsisdnToken(
  186. self.hs.config.registration.account_threepid_delegate_msisdn,
  187. country,
  188. phone_number,
  189. client_secret,
  190. send_attempt,
  191. next_link,
  192. )
  193. threepid_send_requests.labels(type="msisdn", reason="register").observe(
  194. send_attempt
  195. )
  196. return 200, ret
  197. class RegistrationSubmitTokenServlet(RestServlet):
  198. """Handles registration 3PID validation token submission"""
  199. PATTERNS = client_patterns(
  200. "/registration/(?P<medium>[^/]*)/submit_token$", releases=(), unstable=True
  201. )
  202. def __init__(self, hs: "HomeServer"):
  203. super().__init__()
  204. self.hs = hs
  205. self.auth = hs.get_auth()
  206. self.config = hs.config
  207. self.clock = hs.get_clock()
  208. self.store = hs.get_datastores().main
  209. if self.config.email.can_verify_email:
  210. self._failure_email_template = (
  211. self.config.email.email_registration_template_failure_html
  212. )
  213. async def on_GET(self, request: Request, medium: str) -> None:
  214. if medium != "email":
  215. raise SynapseError(
  216. 400, "This medium is currently not supported for registration"
  217. )
  218. if not self.config.email.can_verify_email:
  219. logger.warning(
  220. "User registration via email has been disabled due to lack of email config"
  221. )
  222. raise SynapseError(
  223. 400, "Email-based registration is disabled on this server"
  224. )
  225. sid = parse_string(request, "sid", required=True)
  226. client_secret = parse_string(request, "client_secret", required=True)
  227. assert_valid_client_secret(client_secret)
  228. token = parse_string(request, "token", required=True)
  229. # Attempt to validate a 3PID session
  230. try:
  231. # Mark the session as valid
  232. next_link = await self.store.validate_threepid_session(
  233. sid, client_secret, token, self.clock.time_msec()
  234. )
  235. # Perform a 302 redirect if next_link is set
  236. if next_link:
  237. if next_link.startswith("file:///"):
  238. logger.warning(
  239. "Not redirecting to next_link as it is a local file: address"
  240. )
  241. else:
  242. request.setResponseCode(302)
  243. request.setHeader("Location", next_link)
  244. finish_request(request)
  245. return None
  246. # Otherwise show the success template
  247. html = self.config.email.email_registration_template_success_html_content
  248. status_code = 200
  249. except ThreepidValidationError as e:
  250. status_code = e.code
  251. # Show a failure page with a reason
  252. template_vars = {"failure_reason": e.msg}
  253. html = self._failure_email_template.render(**template_vars)
  254. respond_with_html(request, status_code, html)
  255. class UsernameAvailabilityRestServlet(RestServlet):
  256. PATTERNS = client_patterns("/register/available")
  257. def __init__(self, hs: "HomeServer"):
  258. super().__init__()
  259. self.hs = hs
  260. self.registration_handler = hs.get_registration_handler()
  261. self.ratelimiter = FederationRateLimiter(
  262. hs.get_clock(),
  263. FederationRatelimitSettings(
  264. # Time window of 2s
  265. window_size=2000,
  266. # Artificially delay requests if rate > sleep_limit/window_size
  267. sleep_limit=1,
  268. # Amount of artificial delay to apply
  269. sleep_delay=1000,
  270. # Error with 429 if more than reject_limit requests are queued
  271. reject_limit=1,
  272. # Allow 1 request at a time
  273. concurrent=1,
  274. ),
  275. )
  276. self.inhibit_user_in_use_error = (
  277. hs.config.registration.inhibit_user_in_use_error
  278. )
  279. async def on_GET(self, request: Request) -> Tuple[int, JsonDict]:
  280. if not self.hs.config.registration.enable_registration:
  281. raise SynapseError(
  282. 403, "Registration has been disabled", errcode=Codes.FORBIDDEN
  283. )
  284. if self.inhibit_user_in_use_error:
  285. return 200, {"available": True}
  286. ip = request.getClientAddress().host
  287. with self.ratelimiter.ratelimit(ip) as wait_deferred:
  288. await wait_deferred
  289. username = parse_string(request, "username", required=True)
  290. await self.registration_handler.check_username(username)
  291. return 200, {"available": True}
  292. class RegistrationTokenValidityRestServlet(RestServlet):
  293. """Check the validity of a registration token.
  294. Example:
  295. GET /_matrix/client/v1/register/m.login.registration_token/validity?token=abcd
  296. 200 OK
  297. {
  298. "valid": true
  299. }
  300. """
  301. PATTERNS = client_patterns(
  302. f"/register/{LoginType.REGISTRATION_TOKEN}/validity",
  303. releases=("v1",),
  304. )
  305. def __init__(self, hs: "HomeServer"):
  306. super().__init__()
  307. self.hs = hs
  308. self.store = hs.get_datastores().main
  309. self.ratelimiter = Ratelimiter(
  310. store=self.store,
  311. clock=hs.get_clock(),
  312. rate_hz=hs.config.ratelimiting.rc_registration_token_validity.per_second,
  313. burst_count=hs.config.ratelimiting.rc_registration_token_validity.burst_count,
  314. )
  315. async def on_GET(self, request: Request) -> Tuple[int, JsonDict]:
  316. await self.ratelimiter.ratelimit(None, (request.getClientAddress().host,))
  317. if not self.hs.config.registration.enable_registration:
  318. raise SynapseError(
  319. 403, "Registration has been disabled", errcode=Codes.FORBIDDEN
  320. )
  321. token = parse_string(request, "token", required=True)
  322. valid = await self.store.registration_token_is_valid(token)
  323. return 200, {"valid": valid}
  324. class RegisterRestServlet(RestServlet):
  325. PATTERNS = client_patterns("/register$")
  326. def __init__(self, hs: "HomeServer"):
  327. super().__init__()
  328. self.hs = hs
  329. self.auth = hs.get_auth()
  330. self.store = hs.get_datastores().main
  331. self.auth_handler = hs.get_auth_handler()
  332. self.registration_handler = hs.get_registration_handler()
  333. self.identity_handler = hs.get_identity_handler()
  334. self.room_member_handler = hs.get_room_member_handler()
  335. self.macaroon_gen = hs.get_macaroon_generator()
  336. self.ratelimiter = hs.get_registration_ratelimiter()
  337. self.password_policy_handler = hs.get_password_policy_handler()
  338. self.clock = hs.get_clock()
  339. self.password_auth_provider = hs.get_password_auth_provider()
  340. self._registration_enabled = self.hs.config.registration.enable_registration
  341. self._refresh_tokens_enabled = (
  342. hs.config.registration.refreshable_access_token_lifetime is not None
  343. )
  344. self._inhibit_user_in_use_error = (
  345. hs.config.registration.inhibit_user_in_use_error
  346. )
  347. self._require_approval = (
  348. hs.config.experimental.msc3866.enabled
  349. and hs.config.experimental.msc3866.require_approval_for_new_accounts
  350. )
  351. self._registration_flows = _calculate_registration_flows(
  352. hs.config, self.auth_handler
  353. )
  354. @interactive_auth_handler
  355. async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
  356. body = parse_json_object_from_request(request)
  357. client_addr = request.getClientAddress().host
  358. await self.ratelimiter.ratelimit(None, client_addr, update=False)
  359. kind = parse_string(request, "kind", default="user")
  360. if kind == "guest":
  361. ret = await self._do_guest_registration(body, address=client_addr)
  362. return ret
  363. elif kind != "user":
  364. raise UnrecognizedRequestError(
  365. f"Do not understand membership kind: {kind}",
  366. )
  367. # Check if the clients wishes for this registration to issue a refresh
  368. # token.
  369. client_requested_refresh_tokens = body.get("refresh_token", False)
  370. if not isinstance(client_requested_refresh_tokens, bool):
  371. raise SynapseError(400, "`refresh_token` should be true or false.")
  372. should_issue_refresh_token = (
  373. self._refresh_tokens_enabled and client_requested_refresh_tokens
  374. )
  375. # Pull out the provided username and do basic sanity checks early since
  376. # the auth layer will store these in sessions.
  377. desired_username = None
  378. if "username" in body:
  379. if not isinstance(body["username"], str) or len(body["username"]) > 512:
  380. raise SynapseError(400, "Invalid username")
  381. desired_username = body["username"]
  382. # fork off as soon as possible for ASes which have completely
  383. # different registration flows to normal users
  384. # == Application Service Registration ==
  385. if body.get("type") == APP_SERVICE_REGISTRATION_TYPE:
  386. if not self.auth.has_access_token(request):
  387. raise SynapseError(
  388. 400,
  389. "Appservice token must be provided when using a type of m.login.application_service",
  390. )
  391. # Set the desired user according to the AS API (which uses the
  392. # 'user' key not 'username'). Since this is a new addition, we'll
  393. # fallback to 'username' if they gave one.
  394. desired_username = body.get("user", desired_username)
  395. # XXX we should check that desired_username is valid. Currently
  396. # we give appservices carte blanche for any insanity in mxids,
  397. # because the IRC bridges rely on being able to register stupid
  398. # IDs.
  399. access_token = self.auth.get_access_token_from_request(request)
  400. if not isinstance(desired_username, str):
  401. raise SynapseError(400, "Desired Username is missing or not a string")
  402. result = await self._do_appservice_registration(
  403. desired_username,
  404. access_token,
  405. body,
  406. should_issue_refresh_token=should_issue_refresh_token,
  407. )
  408. return 200, result
  409. elif self.auth.has_access_token(request):
  410. raise SynapseError(
  411. 400,
  412. "An access token should not be provided on requests to /register (except if type is m.login.application_service)",
  413. )
  414. # == Normal User Registration == (everyone else)
  415. if not self._registration_enabled:
  416. raise SynapseError(403, "Registration has been disabled", Codes.FORBIDDEN)
  417. # For regular registration, convert the provided username to lowercase
  418. # before attempting to register it. This should mean that people who try
  419. # to register with upper-case in their usernames don't get a nasty surprise.
  420. #
  421. # Note that we treat usernames case-insensitively in login, so they are
  422. # free to carry on imagining that their username is CrAzYh4cKeR if that
  423. # keeps them happy.
  424. if desired_username is not None:
  425. desired_username = desired_username.lower()
  426. # Check if this account is upgrading from a guest account.
  427. guest_access_token = body.get("guest_access_token", None)
  428. # Pull out the provided password and do basic sanity checks early.
  429. #
  430. # Note that we remove the password from the body since the auth layer
  431. # will store the body in the session and we don't want a plaintext
  432. # password store there.
  433. password = body.pop("password", None)
  434. if password is not None:
  435. if not isinstance(password, str) or len(password) > 512:
  436. raise SynapseError(400, "Invalid password")
  437. self.password_policy_handler.validate_password(password)
  438. if "initial_device_display_name" in body and password is None:
  439. # ignore 'initial_device_display_name' if sent without
  440. # a password to work around a client bug where it sent
  441. # the 'initial_device_display_name' param alone, wiping out
  442. # the original registration params
  443. logger.warning("Ignoring initial_device_display_name without password")
  444. del body["initial_device_display_name"]
  445. session_id = self.auth_handler.get_session_id(body)
  446. registered_user_id = None
  447. password_hash = None
  448. if session_id:
  449. # if we get a registered user id out of here, it means we previously
  450. # registered a user for this session, so we could just return the
  451. # user here. We carry on and go through the auth checks though,
  452. # for paranoia.
  453. registered_user_id = await self.auth_handler.get_session_data(
  454. session_id, UIAuthSessionDataConstants.REGISTERED_USER_ID, None
  455. )
  456. # Extract the previously-hashed password from the session.
  457. password_hash = await self.auth_handler.get_session_data(
  458. session_id, UIAuthSessionDataConstants.PASSWORD_HASH, None
  459. )
  460. # Ensure that the username is valid.
  461. if desired_username is not None:
  462. await self.registration_handler.check_username(
  463. desired_username,
  464. guest_access_token=guest_access_token,
  465. assigned_user_id=registered_user_id,
  466. inhibit_user_in_use_error=self._inhibit_user_in_use_error,
  467. )
  468. # Check if the user-interactive authentication flows are complete, if
  469. # not this will raise a user-interactive auth error.
  470. try:
  471. auth_result, params, session_id = await self.auth_handler.check_ui_auth(
  472. self._registration_flows,
  473. request,
  474. body,
  475. "register a new account",
  476. )
  477. except InteractiveAuthIncompleteError as e:
  478. # The user needs to provide more steps to complete auth.
  479. #
  480. # Hash the password and store it with the session since the client
  481. # is not required to provide the password again.
  482. #
  483. # If a password hash was previously stored we will not attempt to
  484. # re-hash and store it for efficiency. This assumes the password
  485. # does not change throughout the authentication flow, but this
  486. # should be fine since the data is meant to be consistent.
  487. if not password_hash and password:
  488. password_hash = await self.auth_handler.hash(password)
  489. await self.auth_handler.set_session_data(
  490. e.session_id,
  491. UIAuthSessionDataConstants.PASSWORD_HASH,
  492. password_hash,
  493. )
  494. raise
  495. # Check that we're not trying to register a denied 3pid.
  496. #
  497. # the user-facing checks will probably already have happened in
  498. # /register/email/requestToken when we requested a 3pid, but that's not
  499. # guaranteed.
  500. if auth_result:
  501. for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]:
  502. if login_type in auth_result:
  503. medium = auth_result[login_type]["medium"]
  504. address = auth_result[login_type]["address"]
  505. if not await check_3pid_allowed(
  506. self.hs, medium, address, registration=True
  507. ):
  508. raise SynapseError(
  509. 403,
  510. "Third party identifiers (email/phone numbers)"
  511. + " are not authorized on this server",
  512. Codes.THREEPID_DENIED,
  513. )
  514. if registered_user_id is not None:
  515. logger.info(
  516. "Already registered user ID %r for this session", registered_user_id
  517. )
  518. # don't re-register the threepids
  519. registered = False
  520. else:
  521. # If we have a password in this request, prefer it. Otherwise, there
  522. # might be a password hash from an earlier request.
  523. if password:
  524. password_hash = await self.auth_handler.hash(password)
  525. if not password_hash:
  526. raise SynapseError(400, "Missing params: password", Codes.MISSING_PARAM)
  527. desired_username = (
  528. await (
  529. self.password_auth_provider.get_username_for_registration(
  530. auth_result,
  531. params,
  532. )
  533. )
  534. )
  535. if desired_username is None:
  536. desired_username = params.get("username", None)
  537. guest_access_token = params.get("guest_access_token", None)
  538. if desired_username is not None:
  539. desired_username = desired_username.lower()
  540. threepid = None
  541. if auth_result:
  542. threepid = auth_result.get(LoginType.EMAIL_IDENTITY)
  543. # Also check that we're not trying to register a 3pid that's already
  544. # been registered.
  545. #
  546. # This has probably happened in /register/email/requestToken as well,
  547. # but if a user hits this endpoint twice then clicks on each link from
  548. # the two activation emails, they would register the same 3pid twice.
  549. for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]:
  550. if login_type in auth_result:
  551. medium = auth_result[login_type]["medium"]
  552. address = auth_result[login_type]["address"]
  553. # For emails, canonicalise the address.
  554. # We store all email addresses canonicalised in the DB.
  555. # (See on_POST in EmailThreepidRequestTokenRestServlet
  556. # in synapse/rest/client/account.py)
  557. if medium == "email":
  558. try:
  559. address = canonicalise_email(address)
  560. except ValueError as e:
  561. raise SynapseError(400, str(e))
  562. existing_user_id = await self.store.get_user_id_by_threepid(
  563. medium, address
  564. )
  565. if existing_user_id is not None:
  566. raise SynapseError(
  567. 400,
  568. "%s is already in use" % medium,
  569. Codes.THREEPID_IN_USE,
  570. )
  571. entries = await self.store.get_user_agents_ips_to_ui_auth_session(
  572. session_id
  573. )
  574. display_name = (
  575. await (
  576. self.password_auth_provider.get_displayname_for_registration(
  577. auth_result, params
  578. )
  579. )
  580. )
  581. registered_user_id = await self.registration_handler.register_user(
  582. localpart=desired_username,
  583. password_hash=password_hash,
  584. guest_access_token=guest_access_token,
  585. threepid=threepid,
  586. default_display_name=display_name,
  587. address=client_addr,
  588. user_agent_ips=entries,
  589. )
  590. # Necessary due to auth checks prior to the threepid being
  591. # written to the db
  592. if threepid:
  593. if is_threepid_reserved(
  594. self.hs.config.server.mau_limits_reserved_threepids, threepid
  595. ):
  596. await self.store.upsert_monthly_active_user(registered_user_id)
  597. # Remember that the user account has been registered (and the user
  598. # ID it was registered with, since it might not have been specified).
  599. await self.auth_handler.set_session_data(
  600. session_id,
  601. UIAuthSessionDataConstants.REGISTERED_USER_ID,
  602. registered_user_id,
  603. )
  604. registered = True
  605. return_dict = await self._create_registration_details(
  606. registered_user_id,
  607. params,
  608. should_issue_refresh_token=should_issue_refresh_token,
  609. )
  610. if registered:
  611. # Check if a token was used to authenticate registration
  612. registration_token = await self.auth_handler.get_session_data(
  613. session_id,
  614. UIAuthSessionDataConstants.REGISTRATION_TOKEN,
  615. )
  616. if registration_token:
  617. # Increment the `completed` counter for the token
  618. await self.store.use_registration_token(registration_token)
  619. # Indicate that the token has been successfully used so that
  620. # pending is not decremented again when expiring old UIA sessions.
  621. await self.store.mark_ui_auth_stage_complete(
  622. session_id,
  623. LoginType.REGISTRATION_TOKEN,
  624. True,
  625. )
  626. await self.registration_handler.post_registration_actions(
  627. user_id=registered_user_id,
  628. auth_result=auth_result,
  629. access_token=return_dict.get("access_token"),
  630. )
  631. if self._require_approval:
  632. raise NotApprovedError(
  633. msg="This account needs to be approved by an administrator before it can be used.",
  634. approval_notice_medium=ApprovalNoticeMedium.NONE,
  635. )
  636. return 200, return_dict
  637. async def _do_appservice_registration(
  638. self,
  639. username: str,
  640. as_token: str,
  641. body: JsonDict,
  642. should_issue_refresh_token: bool = False,
  643. ) -> JsonDict:
  644. user_id = await self.registration_handler.appservice_register(
  645. username, as_token
  646. )
  647. return await self._create_registration_details(
  648. user_id,
  649. body,
  650. is_appservice_ghost=True,
  651. should_issue_refresh_token=should_issue_refresh_token,
  652. )
  653. async def _create_registration_details(
  654. self,
  655. user_id: str,
  656. params: JsonDict,
  657. is_appservice_ghost: bool = False,
  658. should_issue_refresh_token: bool = False,
  659. ) -> JsonDict:
  660. """Complete registration of newly-registered user
  661. Allocates device_id if one was not given; also creates access_token.
  662. Args:
  663. user_id: full canonical @user:id
  664. params: registration parameters, from which we pull device_id,
  665. initial_device_name and inhibit_login
  666. is_appservice_ghost
  667. should_issue_refresh_token: True if this registration should issue
  668. a refresh token alongside the access token.
  669. Returns:
  670. dictionary for response from /register
  671. """
  672. result: JsonDict = {
  673. "user_id": user_id,
  674. "home_server": self.hs.hostname,
  675. }
  676. # We don't want to log the user in if we're going to deny them access because
  677. # they need to be approved first.
  678. if not params.get("inhibit_login", False) and not self._require_approval:
  679. device_id = params.get("device_id")
  680. initial_display_name = params.get("initial_device_display_name")
  681. (
  682. device_id,
  683. access_token,
  684. valid_until_ms,
  685. refresh_token,
  686. ) = await self.registration_handler.register_device(
  687. user_id,
  688. device_id,
  689. initial_display_name,
  690. is_guest=False,
  691. is_appservice_ghost=is_appservice_ghost,
  692. should_issue_refresh_token=should_issue_refresh_token,
  693. )
  694. result.update({"access_token": access_token, "device_id": device_id})
  695. if valid_until_ms is not None:
  696. expires_in_ms = valid_until_ms - self.clock.time_msec()
  697. result["expires_in_ms"] = expires_in_ms
  698. if refresh_token is not None:
  699. result["refresh_token"] = refresh_token
  700. return result
  701. async def _do_guest_registration(
  702. self, params: JsonDict, address: Optional[str] = None
  703. ) -> Tuple[int, JsonDict]:
  704. if not self.hs.config.registration.allow_guest_access:
  705. raise SynapseError(403, "Guest access is disabled")
  706. user_id = await self.registration_handler.register_user(
  707. make_guest=True, address=address
  708. )
  709. # we don't allow guests to specify their own device_id, because
  710. # we have nowhere to store it.
  711. device_id = synapse.api.auth.GUEST_DEVICE_ID
  712. initial_display_name = params.get("initial_device_display_name")
  713. (
  714. device_id,
  715. access_token,
  716. valid_until_ms,
  717. refresh_token,
  718. ) = await self.registration_handler.register_device(
  719. user_id, device_id, initial_display_name, is_guest=True
  720. )
  721. result: JsonDict = {
  722. "user_id": user_id,
  723. "device_id": device_id,
  724. "access_token": access_token,
  725. "home_server": self.hs.hostname,
  726. }
  727. if valid_until_ms is not None:
  728. expires_in_ms = valid_until_ms - self.clock.time_msec()
  729. result["expires_in_ms"] = expires_in_ms
  730. if refresh_token is not None:
  731. result["refresh_token"] = refresh_token
  732. return 200, result
  733. def _calculate_registration_flows(
  734. config: HomeServerConfig, auth_handler: AuthHandler
  735. ) -> List[List[str]]:
  736. """Get a suitable flows list for registration
  737. Args:
  738. config: server configuration
  739. auth_handler: authorization handler
  740. Returns: a list of supported flows
  741. """
  742. # FIXME: need a better error than "no auth flow found" for scenarios
  743. # where we required 3PID for registration but the user didn't give one
  744. require_email = "email" in config.registration.registrations_require_3pid
  745. require_msisdn = "msisdn" in config.registration.registrations_require_3pid
  746. show_msisdn = True
  747. show_email = True
  748. if config.registration.disable_msisdn_registration:
  749. show_msisdn = False
  750. require_msisdn = False
  751. enabled_auth_types = auth_handler.get_enabled_auth_types()
  752. if LoginType.EMAIL_IDENTITY not in enabled_auth_types:
  753. show_email = False
  754. if require_email:
  755. raise ConfigError(
  756. "Configuration requires email address at registration, but email "
  757. "validation is not configured"
  758. )
  759. if LoginType.MSISDN not in enabled_auth_types:
  760. show_msisdn = False
  761. if require_msisdn:
  762. raise ConfigError(
  763. "Configuration requires msisdn at registration, but msisdn "
  764. "validation is not configured"
  765. )
  766. flows = []
  767. # only support 3PIDless registration if no 3PIDs are required
  768. if not require_email and not require_msisdn:
  769. # Add a dummy step here, otherwise if a client completes
  770. # recaptcha first we'll assume they were going for this flow
  771. # and complete the request, when they could have been trying to
  772. # complete one of the flows with email/msisdn auth.
  773. flows.append([LoginType.DUMMY])
  774. # only support the email-only flow if we don't require MSISDN 3PIDs
  775. if show_email and not require_msisdn:
  776. flows.append([LoginType.EMAIL_IDENTITY])
  777. # only support the MSISDN-only flow if we don't require email 3PIDs
  778. if show_msisdn and not require_email:
  779. flows.append([LoginType.MSISDN])
  780. if show_email and show_msisdn:
  781. # always let users provide both MSISDN & email
  782. flows.append([LoginType.MSISDN, LoginType.EMAIL_IDENTITY])
  783. # Add a flow that doesn't require any 3pids, if the config requests it.
  784. if config.registration.enable_registration_token_3pid_bypass:
  785. flows.append([LoginType.REGISTRATION_TOKEN])
  786. # Prepend m.login.terms to all flows if we're requiring consent
  787. if config.consent.user_consent_at_registration:
  788. for flow in flows:
  789. flow.insert(0, LoginType.TERMS)
  790. # Prepend recaptcha to all flows if we're requiring captcha
  791. if config.captcha.enable_registration_captcha:
  792. for flow in flows:
  793. flow.insert(0, LoginType.RECAPTCHA)
  794. # Prepend registration token to all flows if we're requiring a token
  795. if config.registration.registration_requires_token:
  796. for flow in flows:
  797. if LoginType.REGISTRATION_TOKEN not in flow:
  798. flow.insert(0, LoginType.REGISTRATION_TOKEN)
  799. return flows
  800. def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
  801. if hs.config.worker.worker_app is None:
  802. EmailRegisterRequestTokenRestServlet(hs).register(http_server)
  803. MsisdnRegisterRequestTokenRestServlet(hs).register(http_server)
  804. UsernameAvailabilityRestServlet(hs).register(http_server)
  805. RegistrationSubmitTokenServlet(hs).register(http_server)
  806. RegistrationTokenValidityRestServlet(hs).register(http_server)
  807. RegisterRestServlet(hs).register(http_server)