Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

1050 строки
42 KiB

  1. # Copyright 2014 - 2016 OpenMarket Ltd
  2. # Copyright 2021 The Matrix.org Foundation C.I.C.
  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 typing import TYPE_CHECKING, Iterable, List, Optional, Tuple
  18. from prometheus_client import Counter
  19. from typing_extensions import TypedDict
  20. from synapse import types
  21. from synapse.api.constants import (
  22. MAX_USERID_LENGTH,
  23. EventContentFields,
  24. EventTypes,
  25. JoinRules,
  26. LoginType,
  27. )
  28. from synapse.api.errors import (
  29. AuthError,
  30. Codes,
  31. ConsentNotGivenError,
  32. InvalidClientTokenError,
  33. SynapseError,
  34. )
  35. from synapse.appservice import ApplicationService
  36. from synapse.config.server import is_threepid_reserved
  37. from synapse.handlers.device import DeviceHandler
  38. from synapse.http.servlet import assert_params_in_dict
  39. from synapse.replication.http.login import RegisterDeviceReplicationServlet
  40. from synapse.replication.http.register import (
  41. ReplicationPostRegisterActionsServlet,
  42. ReplicationRegisterServlet,
  43. )
  44. from synapse.spam_checker_api import RegistrationBehaviour
  45. from synapse.types import GUEST_USER_ID_PATTERN, RoomAlias, UserID, create_requester
  46. from synapse.types.state import StateFilter
  47. if TYPE_CHECKING:
  48. from synapse.server import HomeServer
  49. logger = logging.getLogger(__name__)
  50. registration_counter = Counter(
  51. "synapse_user_registrations_total",
  52. "Number of new users registered (since restart)",
  53. ["guest", "shadow_banned", "auth_provider"],
  54. )
  55. login_counter = Counter(
  56. "synapse_user_logins_total",
  57. "Number of user logins (since restart)",
  58. ["guest", "auth_provider"],
  59. )
  60. def init_counters_for_auth_provider(auth_provider_id: str) -> None:
  61. """Ensure the prometheus counters for the given auth provider are initialised
  62. This fixes a problem where the counters are not reported for a given auth provider
  63. until the user first logs in/registers.
  64. """
  65. for is_guest in (True, False):
  66. login_counter.labels(guest=is_guest, auth_provider=auth_provider_id)
  67. for shadow_banned in (True, False):
  68. registration_counter.labels(
  69. guest=is_guest,
  70. shadow_banned=shadow_banned,
  71. auth_provider=auth_provider_id,
  72. )
  73. class LoginDict(TypedDict):
  74. device_id: str
  75. access_token: str
  76. valid_until_ms: Optional[int]
  77. refresh_token: Optional[str]
  78. class RegistrationHandler:
  79. def __init__(self, hs: "HomeServer"):
  80. self.store = hs.get_datastores().main
  81. self._storage_controllers = hs.get_storage_controllers()
  82. self.clock = hs.get_clock()
  83. self.hs = hs
  84. self.auth = hs.get_auth()
  85. self.auth_blocking = hs.get_auth_blocking()
  86. self._auth_handler = hs.get_auth_handler()
  87. self.profile_handler = hs.get_profile_handler()
  88. self.user_directory_handler = hs.get_user_directory_handler()
  89. self.identity_handler = self.hs.get_identity_handler()
  90. self.ratelimiter = hs.get_registration_ratelimiter()
  91. self.macaroon_gen = hs.get_macaroon_generator()
  92. self._account_validity_handler = hs.get_account_validity_handler()
  93. self._user_consent_version = self.hs.config.consent.user_consent_version
  94. self._server_notices_mxid = hs.config.servernotices.server_notices_mxid
  95. self._server_name = hs.hostname
  96. self._spam_checker_module_callbacks = hs.get_module_api_callbacks().spam_checker
  97. if hs.config.worker.worker_app:
  98. self._register_client = ReplicationRegisterServlet.make_client(hs)
  99. self._register_device_client = RegisterDeviceReplicationServlet.make_client(
  100. hs
  101. )
  102. self._post_registration_client = (
  103. ReplicationPostRegisterActionsServlet.make_client(hs)
  104. )
  105. else:
  106. self.device_handler = hs.get_device_handler()
  107. self._register_device_client = self.register_device_inner
  108. self.pusher_pool = hs.get_pusherpool()
  109. self.session_lifetime = hs.config.registration.session_lifetime
  110. self.nonrefreshable_access_token_lifetime = (
  111. hs.config.registration.nonrefreshable_access_token_lifetime
  112. )
  113. self.refreshable_access_token_lifetime = (
  114. hs.config.registration.refreshable_access_token_lifetime
  115. )
  116. self.refresh_token_lifetime = hs.config.registration.refresh_token_lifetime
  117. init_counters_for_auth_provider("")
  118. async def check_username(
  119. self,
  120. localpart: str,
  121. guest_access_token: Optional[str] = None,
  122. assigned_user_id: Optional[str] = None,
  123. inhibit_user_in_use_error: bool = False,
  124. ) -> None:
  125. if types.contains_invalid_mxid_characters(localpart):
  126. raise SynapseError(
  127. 400,
  128. "User ID can only contain characters a-z, 0-9, or '=_-./+'",
  129. Codes.INVALID_USERNAME,
  130. )
  131. if not localpart:
  132. raise SynapseError(400, "User ID cannot be empty", Codes.INVALID_USERNAME)
  133. if localpart[0] == "_":
  134. raise SynapseError(
  135. 400, "User ID may not begin with _", Codes.INVALID_USERNAME
  136. )
  137. user = UserID(localpart, self.hs.hostname)
  138. user_id = user.to_string()
  139. if assigned_user_id:
  140. if user_id == assigned_user_id:
  141. return
  142. else:
  143. raise SynapseError(
  144. 400,
  145. "A different user ID has already been registered for this session",
  146. )
  147. self.check_user_id_not_appservice_exclusive(user_id)
  148. if len(user_id) > MAX_USERID_LENGTH:
  149. raise SynapseError(
  150. 400,
  151. "User ID may not be longer than %s characters" % (MAX_USERID_LENGTH,),
  152. Codes.INVALID_USERNAME,
  153. )
  154. users = await self.store.get_users_by_id_case_insensitive(user_id)
  155. if users:
  156. if not inhibit_user_in_use_error and not guest_access_token:
  157. raise SynapseError(
  158. 400, "User ID already taken.", errcode=Codes.USER_IN_USE
  159. )
  160. if guest_access_token:
  161. user_data = await self.auth.get_user_by_access_token(guest_access_token)
  162. if not user_data.is_guest or user_data.user.localpart != localpart:
  163. raise AuthError(
  164. 403,
  165. "Cannot register taken user ID without valid guest "
  166. "credentials for that user.",
  167. errcode=Codes.FORBIDDEN,
  168. )
  169. if guest_access_token is None and GUEST_USER_ID_PATTERN.fullmatch(localpart):
  170. raise SynapseError(
  171. 400,
  172. "Numeric user IDs are reserved for guest users.",
  173. errcode=Codes.INVALID_USERNAME,
  174. )
  175. async def register_user(
  176. self,
  177. localpart: Optional[str] = None,
  178. password_hash: Optional[str] = None,
  179. guest_access_token: Optional[str] = None,
  180. make_guest: bool = False,
  181. admin: bool = False,
  182. threepid: Optional[dict] = None,
  183. user_type: Optional[str] = None,
  184. default_display_name: Optional[str] = None,
  185. address: Optional[str] = None,
  186. bind_emails: Optional[Iterable[str]] = None,
  187. by_admin: bool = False,
  188. user_agent_ips: Optional[List[Tuple[str, str]]] = None,
  189. auth_provider_id: Optional[str] = None,
  190. approved: bool = False,
  191. ) -> str:
  192. """Registers a new client on the server.
  193. Args:
  194. localpart: The local part of the user ID to register. If None,
  195. one will be generated.
  196. password_hash: The hashed password to assign to this user so they can
  197. login again. This can be None which means they cannot login again
  198. via a password (e.g. the user is an application service user).
  199. guest_access_token: The access token used when this was a guest
  200. account.
  201. make_guest: True if the the new user should be guest,
  202. false to add a regular user account.
  203. admin: True if the user should be registered as a server admin.
  204. threepid: The threepid used for registering, if any.
  205. user_type: type of user. One of the values from
  206. api.constants.UserTypes, or None for a normal user.
  207. default_display_name: if set, the new user's displayname
  208. will be set to this. Defaults to 'localpart'.
  209. address: the IP address used to perform the registration.
  210. bind_emails: list of emails to bind to this account.
  211. by_admin: True if this registration is being made via the
  212. admin api, otherwise False.
  213. user_agent_ips: Tuples of user-agents and IP addresses used
  214. during the registration process.
  215. auth_provider_id: The SSO IdP the user used, if any.
  216. approved: True if the new user should be considered already
  217. approved by an administrator.
  218. Returns:
  219. The registered user_id.
  220. Raises:
  221. SynapseError if there was a problem registering.
  222. """
  223. bind_emails = bind_emails or []
  224. await self.check_registration_ratelimit(address)
  225. result = await self._spam_checker_module_callbacks.check_registration_for_spam(
  226. threepid,
  227. localpart,
  228. user_agent_ips or [],
  229. auth_provider_id=auth_provider_id,
  230. )
  231. if result == RegistrationBehaviour.DENY:
  232. logger.info(
  233. "Blocked registration of %r",
  234. localpart,
  235. )
  236. # We return a 429 to make it not obvious that they've been
  237. # denied.
  238. raise SynapseError(429, "Rate limited")
  239. shadow_banned = result == RegistrationBehaviour.SHADOW_BAN
  240. if shadow_banned:
  241. logger.info(
  242. "Shadow banning registration of %r",
  243. localpart,
  244. )
  245. # do not check_auth_blocking if the call is coming through the Admin API
  246. if not by_admin:
  247. await self.auth_blocking.check_auth_blocking(threepid=threepid)
  248. if localpart is not None:
  249. await self.check_username(localpart, guest_access_token=guest_access_token)
  250. was_guest = guest_access_token is not None
  251. user = UserID(localpart, self.hs.hostname)
  252. user_id = user.to_string()
  253. if was_guest:
  254. # If the user was a guest then they already have a profile
  255. default_display_name = None
  256. elif default_display_name is None:
  257. default_display_name = localpart
  258. await self.register_with_store(
  259. user_id=user_id,
  260. password_hash=password_hash,
  261. was_guest=was_guest,
  262. make_guest=make_guest,
  263. create_profile_with_displayname=default_display_name,
  264. admin=admin,
  265. user_type=user_type,
  266. address=address,
  267. shadow_banned=shadow_banned,
  268. approved=approved,
  269. )
  270. profile = await self.store.get_profileinfo(user)
  271. await self.user_directory_handler.handle_local_profile_change(
  272. user_id, profile
  273. )
  274. else:
  275. # autogen a sequential user ID
  276. fail_count = 0
  277. # If a default display name is not given, generate one.
  278. generate_display_name = default_display_name is None
  279. # This breaks on successful registration *or* errors after 10 failures.
  280. while True:
  281. # Fail after being unable to find a suitable ID a few times
  282. if fail_count > 10:
  283. raise SynapseError(500, "Unable to find a suitable guest user ID")
  284. generated_localpart = await self.store.generate_user_id()
  285. user = UserID(generated_localpart, self.hs.hostname)
  286. user_id = user.to_string()
  287. self.check_user_id_not_appservice_exclusive(user_id)
  288. if generate_display_name:
  289. default_display_name = generated_localpart
  290. try:
  291. await self.register_with_store(
  292. user_id=user_id,
  293. password_hash=password_hash,
  294. make_guest=make_guest,
  295. create_profile_with_displayname=default_display_name,
  296. address=address,
  297. shadow_banned=shadow_banned,
  298. )
  299. # Successfully registered
  300. break
  301. except SynapseError:
  302. # if user id is taken, just generate another
  303. fail_count += 1
  304. registration_counter.labels(
  305. guest=make_guest,
  306. shadow_banned=shadow_banned,
  307. auth_provider=(auth_provider_id or ""),
  308. ).inc()
  309. # If the user does not need to consent at registration, auto-join any
  310. # configured rooms.
  311. if not self.hs.config.consent.user_consent_at_registration:
  312. if (
  313. not self.hs.config.registration.auto_join_rooms_for_guests
  314. and make_guest
  315. ):
  316. logger.info(
  317. "Skipping auto-join for %s because auto-join for guests is disabled",
  318. user_id,
  319. )
  320. else:
  321. await self._auto_join_rooms(user_id)
  322. else:
  323. logger.info(
  324. "Skipping auto-join for %s because consent is required at registration",
  325. user_id,
  326. )
  327. # Bind any specified emails to this account
  328. current_time = self.hs.get_clock().time_msec()
  329. for email in bind_emails:
  330. # generate threepid dict
  331. threepid_dict = {
  332. "medium": "email",
  333. "address": email,
  334. "validated_at": current_time,
  335. }
  336. # Bind email to new account
  337. await self._register_email_threepid(user_id, threepid_dict, None)
  338. return user_id
  339. async def _create_and_join_rooms(self, user_id: str) -> None:
  340. """
  341. Create the auto-join rooms and join or invite the user to them.
  342. This should only be called when the first "real" user registers.
  343. Args:
  344. user_id: The user to join
  345. """
  346. # Getting the handlers during init gives a dependency loop.
  347. room_creation_handler = self.hs.get_room_creation_handler()
  348. room_member_handler = self.hs.get_room_member_handler()
  349. # Generate a stub for how the rooms will be configured.
  350. stub_config = {
  351. "preset": self.hs.config.registration.autocreate_auto_join_room_preset,
  352. }
  353. # If the configuration provides a user ID to create rooms with, use
  354. # that instead of the first user registered.
  355. requires_join = False
  356. if self.hs.config.registration.auto_join_user_id:
  357. fake_requester = create_requester(
  358. self.hs.config.registration.auto_join_user_id,
  359. authenticated_entity=self._server_name,
  360. )
  361. # If the room requires an invite, add the user to the list of invites.
  362. if self.hs.config.registration.auto_join_room_requires_invite:
  363. stub_config["invite"] = [user_id]
  364. # If the room is being created by a different user, the first user
  365. # registered needs to join it. Note that in the case of an invitation
  366. # being necessary this will occur after the invite was sent.
  367. requires_join = True
  368. else:
  369. fake_requester = create_requester(
  370. user_id, authenticated_entity=self._server_name
  371. )
  372. # Choose whether to federate the new room.
  373. if not self.hs.config.registration.autocreate_auto_join_rooms_federated:
  374. stub_config["creation_content"] = {EventContentFields.FEDERATE: False}
  375. for r in self.hs.config.registration.auto_join_rooms:
  376. logger.info("Auto-joining %s to %s", user_id, r)
  377. try:
  378. room_alias = RoomAlias.from_string(r)
  379. if self.hs.hostname != room_alias.domain:
  380. # If the alias is remote, try to join the room. This might fail
  381. # because the room might be invite only, but we don't have any local
  382. # user in the room to invite this one with, so at this point that's
  383. # the best we can do.
  384. logger.info(
  385. "Cannot automatically create room with alias %s as it isn't"
  386. " local, trying to join the room instead",
  387. r,
  388. )
  389. (
  390. room,
  391. remote_room_hosts,
  392. ) = await room_member_handler.lookup_room_alias(room_alias)
  393. room_id = room.to_string()
  394. await room_member_handler.update_membership(
  395. requester=create_requester(
  396. user_id, authenticated_entity=self._server_name
  397. ),
  398. target=UserID.from_string(user_id),
  399. room_id=room_id,
  400. remote_room_hosts=remote_room_hosts,
  401. action="join",
  402. ratelimit=False,
  403. )
  404. else:
  405. # A shallow copy is OK here since the only key that is
  406. # modified is room_alias_name.
  407. config = stub_config.copy()
  408. # create room expects the localpart of the room alias
  409. config["room_alias_name"] = room_alias.localpart
  410. room_id, _, _ = await room_creation_handler.create_room(
  411. fake_requester,
  412. config=config,
  413. ratelimit=False,
  414. )
  415. # If the room does not require an invite, but another user
  416. # created it, then ensure the first user joins it.
  417. if requires_join:
  418. await room_member_handler.update_membership(
  419. requester=create_requester(
  420. user_id, authenticated_entity=self._server_name
  421. ),
  422. target=UserID.from_string(user_id),
  423. room_id=room_id,
  424. # Since it was just created, there are no remote hosts.
  425. remote_room_hosts=[],
  426. action="join",
  427. ratelimit=False,
  428. )
  429. except Exception as e:
  430. logger.error("Failed to join new user to %r: %r", r, e)
  431. async def _join_rooms(self, user_id: str) -> None:
  432. """
  433. Join or invite the user to the auto-join rooms.
  434. Args:
  435. user_id: The user to join
  436. """
  437. room_member_handler = self.hs.get_room_member_handler()
  438. for r in self.hs.config.registration.auto_join_rooms:
  439. logger.info("Auto-joining %s to %s", user_id, r)
  440. try:
  441. room_alias = RoomAlias.from_string(r)
  442. if RoomAlias.is_valid(r):
  443. (
  444. room,
  445. remote_room_hosts,
  446. ) = await room_member_handler.lookup_room_alias(room_alias)
  447. room_id = room.to_string()
  448. else:
  449. raise SynapseError(
  450. 400, "%s was not legal room ID or room alias" % (r,)
  451. )
  452. # Calculate whether the room requires an invite or can be
  453. # joined directly. By default, we consider the room as requiring an
  454. # invite if the homeserver is in the room (unless told otherwise by the
  455. # join rules). Otherwise we consider it as being joinable, at the risk of
  456. # failing to join, but in this case there's little more we can do since
  457. # we don't have a local user in the room to craft up an invite with.
  458. requires_invite = await self.store.is_host_joined(
  459. room_id,
  460. self._server_name,
  461. )
  462. if requires_invite:
  463. # If the server is in the room, check if the room is public.
  464. state = await self._storage_controllers.state.get_current_state_ids(
  465. room_id, StateFilter.from_types([(EventTypes.JoinRules, "")])
  466. )
  467. event_id = state.get((EventTypes.JoinRules, ""))
  468. if event_id:
  469. join_rules_event = await self.store.get_event(
  470. event_id, allow_none=True
  471. )
  472. if join_rules_event:
  473. join_rule = join_rules_event.content.get("join_rule", None)
  474. requires_invite = (
  475. join_rule and join_rule != JoinRules.PUBLIC
  476. )
  477. # Send the invite, if necessary.
  478. if requires_invite:
  479. # If an invite is required, there must be a auto-join user ID.
  480. assert self.hs.config.registration.auto_join_user_id
  481. await room_member_handler.update_membership(
  482. requester=create_requester(
  483. self.hs.config.registration.auto_join_user_id,
  484. authenticated_entity=self._server_name,
  485. ),
  486. target=UserID.from_string(user_id),
  487. room_id=room_id,
  488. remote_room_hosts=remote_room_hosts,
  489. action="invite",
  490. ratelimit=False,
  491. )
  492. # Send the join.
  493. await room_member_handler.update_membership(
  494. requester=create_requester(
  495. user_id, authenticated_entity=self._server_name
  496. ),
  497. target=UserID.from_string(user_id),
  498. room_id=room_id,
  499. remote_room_hosts=remote_room_hosts,
  500. action="join",
  501. ratelimit=False,
  502. )
  503. except ConsentNotGivenError as e:
  504. # Technically not necessary to pull out this error though
  505. # moving away from bare excepts is a good thing to do.
  506. logger.error("Failed to join new user to %r: %r", r, e)
  507. except Exception as e:
  508. logger.error("Failed to join new user to %r: %r", r, e)
  509. async def _auto_join_rooms(self, user_id: str) -> None:
  510. """Automatically joins users to auto join rooms - creating the room in the first place
  511. if the user is the first to be created.
  512. Args:
  513. user_id: The user to join
  514. """
  515. # If there are no rooms to auto-join, just bail.
  516. if not self.hs.config.registration.auto_join_rooms:
  517. return
  518. # auto-join the user to any rooms we're supposed to dump them into
  519. # try to create the room if we're the first real user on the server. Note
  520. # that an auto-generated support or bot user is not a real user and will never be
  521. # the user to create the room
  522. should_auto_create_rooms = False
  523. if (
  524. self.hs.config.registration.autocreate_auto_join_rooms
  525. and await self.store.is_real_user(user_id)
  526. ):
  527. count = await self.store.count_real_users()
  528. should_auto_create_rooms = count == 1
  529. if should_auto_create_rooms:
  530. await self._create_and_join_rooms(user_id)
  531. else:
  532. await self._join_rooms(user_id)
  533. async def post_consent_actions(self, user_id: str) -> None:
  534. """A series of registration actions that can only be carried out once consent
  535. has been granted
  536. Args:
  537. user_id: The user to join
  538. """
  539. await self._auto_join_rooms(user_id)
  540. async def appservice_register(self, user_localpart: str, as_token: str) -> str:
  541. user = UserID(user_localpart, self.hs.hostname)
  542. user_id = user.to_string()
  543. service = self.store.get_app_service_by_token(as_token)
  544. if not service:
  545. raise InvalidClientTokenError()
  546. if not service.is_interested_in_user(user_id):
  547. raise SynapseError(
  548. 400,
  549. "Invalid user localpart for this application service.",
  550. errcode=Codes.EXCLUSIVE,
  551. )
  552. service_id = service.id if service.is_exclusive_user(user_id) else None
  553. self.check_user_id_not_appservice_exclusive(user_id, allowed_appservice=service)
  554. await self.register_with_store(
  555. user_id=user_id,
  556. password_hash="",
  557. appservice_id=service_id,
  558. create_profile_with_displayname=user.localpart,
  559. )
  560. return user_id
  561. def check_user_id_not_appservice_exclusive(
  562. self, user_id: str, allowed_appservice: Optional[ApplicationService] = None
  563. ) -> None:
  564. # don't allow people to register the server notices mxid
  565. if self._server_notices_mxid is not None:
  566. if user_id == self._server_notices_mxid:
  567. raise SynapseError(
  568. 400, "This user ID is reserved.", errcode=Codes.EXCLUSIVE
  569. )
  570. # valid user IDs must not clash with any user ID namespaces claimed by
  571. # application services.
  572. services = self.store.get_app_services()
  573. interested_services = [
  574. s
  575. for s in services
  576. if s.is_interested_in_user(user_id) and s != allowed_appservice
  577. ]
  578. for service in interested_services:
  579. if service.is_exclusive_user(user_id):
  580. raise SynapseError(
  581. 400,
  582. "This user ID is reserved by an application service.",
  583. errcode=Codes.EXCLUSIVE,
  584. )
  585. async def check_registration_ratelimit(self, address: Optional[str]) -> None:
  586. """A simple helper method to check whether the registration rate limit has been hit
  587. for a given IP address
  588. Args:
  589. address: the IP address used to perform the registration. If this is
  590. None, no ratelimiting will be performed.
  591. Raises:
  592. LimitExceededError: If the rate limit has been exceeded.
  593. """
  594. if not address:
  595. return
  596. await self.ratelimiter.ratelimit(None, address)
  597. async def register_with_store(
  598. self,
  599. user_id: str,
  600. password_hash: Optional[str] = None,
  601. was_guest: bool = False,
  602. make_guest: bool = False,
  603. appservice_id: Optional[str] = None,
  604. create_profile_with_displayname: Optional[str] = None,
  605. admin: bool = False,
  606. user_type: Optional[str] = None,
  607. address: Optional[str] = None,
  608. shadow_banned: bool = False,
  609. approved: bool = False,
  610. ) -> None:
  611. """Register user in the datastore.
  612. Args:
  613. user_id: The desired user ID to register.
  614. password_hash: Optional. The password hash for this user.
  615. was_guest: Optional. Whether this is a guest account being
  616. upgraded to a non-guest account.
  617. make_guest: True if the the new user should be guest,
  618. false to add a regular user account.
  619. appservice_id: The ID of the appservice registering the user.
  620. create_profile_with_displayname: Optionally create a
  621. profile for the user, setting their displayname to the given value
  622. admin: is an admin user?
  623. user_type: type of user. One of the values from
  624. api.constants.UserTypes, or None for a normal user.
  625. address: the IP address used to perform the registration.
  626. shadow_banned: Whether to shadow-ban the user
  627. approved: Whether to mark the user as approved by an administrator
  628. """
  629. if self.hs.config.worker.worker_app:
  630. await self._register_client(
  631. user_id=user_id,
  632. password_hash=password_hash,
  633. was_guest=was_guest,
  634. make_guest=make_guest,
  635. appservice_id=appservice_id,
  636. create_profile_with_displayname=create_profile_with_displayname,
  637. admin=admin,
  638. user_type=user_type,
  639. address=address,
  640. shadow_banned=shadow_banned,
  641. approved=approved,
  642. )
  643. else:
  644. await self.store.register_user(
  645. user_id=user_id,
  646. password_hash=password_hash,
  647. was_guest=was_guest,
  648. make_guest=make_guest,
  649. appservice_id=appservice_id,
  650. create_profile_with_displayname=create_profile_with_displayname,
  651. admin=admin,
  652. user_type=user_type,
  653. shadow_banned=shadow_banned,
  654. approved=approved,
  655. )
  656. # Only call the account validity module(s) on the main process, to avoid
  657. # repeating e.g. database writes on all of the workers.
  658. await self._account_validity_handler.on_user_registration(user_id)
  659. async def register_device(
  660. self,
  661. user_id: str,
  662. device_id: Optional[str],
  663. initial_display_name: Optional[str],
  664. is_guest: bool = False,
  665. is_appservice_ghost: bool = False,
  666. auth_provider_id: Optional[str] = None,
  667. should_issue_refresh_token: bool = False,
  668. auth_provider_session_id: Optional[str] = None,
  669. ) -> Tuple[str, str, Optional[int], Optional[str]]:
  670. """Register a device for a user and generate an access token.
  671. The access token will be limited by the homeserver's session_lifetime config.
  672. Args:
  673. user_id: full canonical @user:id
  674. device_id: The device ID to check, or None to generate a new one.
  675. initial_display_name: An optional display name for the device.
  676. is_guest: Whether this is a guest account
  677. auth_provider_id: The SSO IdP the user used, if any.
  678. should_issue_refresh_token: Whether it should also issue a refresh token
  679. auth_provider_session_id: The session ID received during login from the SSO IdP.
  680. Returns:
  681. Tuple of device ID, access token, access token expiration time and refresh token
  682. """
  683. res = await self._register_device_client(
  684. user_id=user_id,
  685. device_id=device_id,
  686. initial_display_name=initial_display_name,
  687. is_guest=is_guest,
  688. is_appservice_ghost=is_appservice_ghost,
  689. should_issue_refresh_token=should_issue_refresh_token,
  690. auth_provider_id=auth_provider_id,
  691. auth_provider_session_id=auth_provider_session_id,
  692. )
  693. login_counter.labels(
  694. guest=is_guest,
  695. auth_provider=(auth_provider_id or ""),
  696. ).inc()
  697. return (
  698. res["device_id"],
  699. res["access_token"],
  700. res["valid_until_ms"],
  701. res["refresh_token"],
  702. )
  703. async def register_device_inner(
  704. self,
  705. user_id: str,
  706. device_id: Optional[str],
  707. initial_display_name: Optional[str],
  708. is_guest: bool = False,
  709. is_appservice_ghost: bool = False,
  710. should_issue_refresh_token: bool = False,
  711. auth_provider_id: Optional[str] = None,
  712. auth_provider_session_id: Optional[str] = None,
  713. ) -> LoginDict:
  714. """Helper for register_device
  715. Does the bits that need doing on the main process. Not for use outside this
  716. class and RegisterDeviceReplicationServlet.
  717. """
  718. assert not self.hs.config.worker.worker_app
  719. now_ms = self.clock.time_msec()
  720. access_token_expiry = None
  721. if self.session_lifetime is not None:
  722. if is_guest:
  723. raise Exception(
  724. "session_lifetime is not currently implemented for guest access"
  725. )
  726. access_token_expiry = now_ms + self.session_lifetime
  727. if self.nonrefreshable_access_token_lifetime is not None:
  728. if access_token_expiry is not None:
  729. # Don't allow the non-refreshable access token to outlive the
  730. # session.
  731. access_token_expiry = min(
  732. now_ms + self.nonrefreshable_access_token_lifetime,
  733. access_token_expiry,
  734. )
  735. else:
  736. access_token_expiry = now_ms + self.nonrefreshable_access_token_lifetime
  737. refresh_token = None
  738. refresh_token_id = None
  739. # This can only run on the main process.
  740. assert isinstance(self.device_handler, DeviceHandler)
  741. registered_device_id = await self.device_handler.check_device_registered(
  742. user_id,
  743. device_id,
  744. initial_display_name,
  745. auth_provider_id=auth_provider_id,
  746. auth_provider_session_id=auth_provider_session_id,
  747. )
  748. if is_guest:
  749. assert access_token_expiry is None
  750. access_token = self.macaroon_gen.generate_guest_access_token(user_id)
  751. else:
  752. if should_issue_refresh_token:
  753. # A refreshable access token lifetime must be configured
  754. # since we're told to issue a refresh token (the caller checks
  755. # that this value is set before setting this flag).
  756. assert self.refreshable_access_token_lifetime is not None
  757. # Set the expiry time of the refreshable access token
  758. access_token_expiry = now_ms + self.refreshable_access_token_lifetime
  759. # Set the refresh token expiry time (if configured)
  760. refresh_token_expiry = None
  761. if self.refresh_token_lifetime is not None:
  762. refresh_token_expiry = now_ms + self.refresh_token_lifetime
  763. # Set an ultimate session expiry time (if configured)
  764. ultimate_session_expiry_ts = None
  765. if self.session_lifetime is not None:
  766. ultimate_session_expiry_ts = now_ms + self.session_lifetime
  767. # Also ensure that the issued tokens don't outlive the
  768. # session.
  769. # (It would be weird to configure a homeserver with a shorter
  770. # session lifetime than token lifetime, but may as well handle
  771. # it.)
  772. access_token_expiry = min(
  773. access_token_expiry, ultimate_session_expiry_ts
  774. )
  775. if refresh_token_expiry is not None:
  776. refresh_token_expiry = min(
  777. refresh_token_expiry, ultimate_session_expiry_ts
  778. )
  779. (
  780. refresh_token,
  781. refresh_token_id,
  782. ) = await self._auth_handler.create_refresh_token_for_user_id(
  783. user_id,
  784. device_id=registered_device_id,
  785. expiry_ts=refresh_token_expiry,
  786. ultimate_session_expiry_ts=ultimate_session_expiry_ts,
  787. )
  788. access_token = await self._auth_handler.create_access_token_for_user_id(
  789. user_id,
  790. device_id=registered_device_id,
  791. valid_until_ms=access_token_expiry,
  792. is_appservice_ghost=is_appservice_ghost,
  793. refresh_token_id=refresh_token_id,
  794. )
  795. return {
  796. "device_id": registered_device_id,
  797. "access_token": access_token,
  798. "valid_until_ms": access_token_expiry,
  799. "refresh_token": refresh_token,
  800. }
  801. async def post_registration_actions(
  802. self, user_id: str, auth_result: dict, access_token: Optional[str]
  803. ) -> None:
  804. """A user has completed registration
  805. Args:
  806. user_id: The user ID that consented
  807. auth_result: The authenticated credentials of the newly registered user.
  808. access_token: The access token of the newly logged in device, or
  809. None if `inhibit_login` enabled.
  810. """
  811. # TODO: 3pid registration can actually happen on the workers. Consider
  812. # refactoring it.
  813. if self.hs.config.worker.worker_app:
  814. await self._post_registration_client(
  815. user_id=user_id, auth_result=auth_result, access_token=access_token
  816. )
  817. return
  818. if auth_result and LoginType.EMAIL_IDENTITY in auth_result:
  819. threepid = auth_result[LoginType.EMAIL_IDENTITY]
  820. # Necessary due to auth checks prior to the threepid being
  821. # written to the db
  822. if is_threepid_reserved(
  823. self.hs.config.server.mau_limits_reserved_threepids, threepid
  824. ):
  825. await self.store.upsert_monthly_active_user(user_id)
  826. await self._register_email_threepid(user_id, threepid, access_token)
  827. if auth_result and LoginType.MSISDN in auth_result:
  828. threepid = auth_result[LoginType.MSISDN]
  829. await self._register_msisdn_threepid(user_id, threepid)
  830. if auth_result and LoginType.TERMS in auth_result:
  831. # The terms type should only exist if consent is enabled.
  832. assert self._user_consent_version is not None
  833. await self._on_user_consented(user_id, self._user_consent_version)
  834. async def _on_user_consented(self, user_id: str, consent_version: str) -> None:
  835. """A user consented to the terms on registration
  836. Args:
  837. user_id: The user ID that consented.
  838. consent_version: version of the policy the user has consented to.
  839. """
  840. logger.info("%s has consented to the privacy policy", user_id)
  841. await self.store.user_set_consent_version(user_id, consent_version)
  842. await self.post_consent_actions(user_id)
  843. async def _register_email_threepid(
  844. self, user_id: str, threepid: dict, token: Optional[str]
  845. ) -> None:
  846. """Add an email address as a 3pid identifier
  847. Also adds an email pusher for the email address, if configured in the
  848. HS config
  849. Must be called on master.
  850. Args:
  851. user_id: id of user
  852. threepid: m.login.email.identity auth response
  853. token: access_token for the user, or None if not logged in.
  854. """
  855. reqd = ("medium", "address", "validated_at")
  856. if any(x not in threepid for x in reqd):
  857. # This will only happen if the ID server returns a malformed response
  858. logger.info("Can't add incomplete 3pid")
  859. return
  860. await self._auth_handler.add_threepid(
  861. user_id,
  862. threepid["medium"],
  863. threepid["address"],
  864. threepid["validated_at"],
  865. )
  866. # And we add an email pusher for them by default, but only
  867. # if email notifications are enabled (so people don't start
  868. # getting mail spam where they weren't before if email
  869. # notifs are set up on a homeserver)
  870. if (
  871. self.hs.config.email.email_enable_notifs
  872. and self.hs.config.email.email_notif_for_new_users
  873. and token
  874. ):
  875. # Pull the ID of the access token back out of the db
  876. # It would really make more sense for this to be passed
  877. # up when the access token is saved, but that's quite an
  878. # invasive change I'd rather do separately.
  879. user_tuple = await self.store.get_user_by_access_token(token)
  880. # The token better still exist.
  881. assert user_tuple
  882. device_id = user_tuple.device_id
  883. await self.pusher_pool.add_or_update_pusher(
  884. user_id=user_id,
  885. device_id=device_id,
  886. kind="email",
  887. app_id="m.email",
  888. app_display_name="Email Notifications",
  889. device_display_name=threepid["address"],
  890. pushkey=threepid["address"],
  891. lang=None,
  892. data={},
  893. )
  894. async def _register_msisdn_threepid(self, user_id: str, threepid: dict) -> None:
  895. """Add a phone number as a 3pid identifier
  896. Must be called on master.
  897. Args:
  898. user_id: id of user
  899. threepid: m.login.msisdn auth response
  900. """
  901. try:
  902. assert_params_in_dict(threepid, ["medium", "address", "validated_at"])
  903. except SynapseError as ex:
  904. if ex.errcode == Codes.MISSING_PARAM:
  905. # This will only happen if the ID server returns a malformed response
  906. logger.info("Can't add incomplete 3pid")
  907. return None
  908. raise
  909. await self._auth_handler.add_threepid(
  910. user_id,
  911. threepid["medium"],
  912. threepid["address"],
  913. threepid["validated_at"],
  914. )