mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-08-18 18:17:47 -04:00
Improve performance of the register endpoint (#8009)
This commit is contained in:
parent
079bc3c8e3
commit
66f24449dd
6 changed files with 146 additions and 74 deletions
|
@ -24,6 +24,7 @@ import synapse.types
|
|||
from synapse.api.constants import LoginType
|
||||
from synapse.api.errors import (
|
||||
Codes,
|
||||
InteractiveAuthIncompleteError,
|
||||
SynapseError,
|
||||
ThreepidValidationError,
|
||||
UnrecognizedRequestError,
|
||||
|
@ -387,6 +388,7 @@ class RegisterRestServlet(RestServlet):
|
|||
self.ratelimiter = hs.get_registration_ratelimiter()
|
||||
self.password_policy_handler = hs.get_password_policy_handler()
|
||||
self.clock = hs.get_clock()
|
||||
self._registration_enabled = self.hs.config.enable_registration
|
||||
|
||||
self._registration_flows = _calculate_registration_flows(
|
||||
hs.config, self.auth_handler
|
||||
|
@ -412,20 +414,8 @@ class RegisterRestServlet(RestServlet):
|
|||
"Do not understand membership kind: %s" % (kind.decode("utf8"),)
|
||||
)
|
||||
|
||||
# we do basic sanity checks here because the auth layer will store these
|
||||
# in sessions. Pull out the username/password provided to us.
|
||||
if "password" in body:
|
||||
password = body.pop("password")
|
||||
if not isinstance(password, str) or len(password) > 512:
|
||||
raise SynapseError(400, "Invalid password")
|
||||
self.password_policy_handler.validate_password(password)
|
||||
|
||||
# If the password is valid, hash it and store it back on the body.
|
||||
# This ensures that only the hashed password is handled everywhere.
|
||||
if "password_hash" in body:
|
||||
raise SynapseError(400, "Unexpected property: password_hash")
|
||||
body["password_hash"] = await self.auth_handler.hash(password)
|
||||
|
||||
# Pull out the provided username and do basic sanity checks early since
|
||||
# the auth layer will store these in sessions.
|
||||
desired_username = None
|
||||
if "username" in body:
|
||||
if not isinstance(body["username"], str) or len(body["username"]) > 512:
|
||||
|
@ -459,22 +449,35 @@ class RegisterRestServlet(RestServlet):
|
|||
)
|
||||
return 200, result # we throw for non 200 responses
|
||||
|
||||
# for regular registration, downcase the provided username before
|
||||
# attempting to register it. This should mean
|
||||
# that people who try to register with upper-case in their usernames
|
||||
# don't get a nasty surprise. (Note that we treat username
|
||||
# case-insenstively in login, so they are free to carry on imagining
|
||||
# that their username is CrAzYh4cKeR if that keeps them happy)
|
||||
# == Normal User Registration == (everyone else)
|
||||
if not self._registration_enabled:
|
||||
raise SynapseError(403, "Registration has been disabled")
|
||||
|
||||
# For regular registration, convert the provided username to lowercase
|
||||
# before attempting to register it. This should mean that people who try
|
||||
# to register with upper-case in their usernames don't get a nasty surprise.
|
||||
#
|
||||
# Note that we treat usernames case-insensitively in login, so they are
|
||||
# free to carry on imagining that their username is CrAzYh4cKeR if that
|
||||
# keeps them happy.
|
||||
if desired_username is not None:
|
||||
desired_username = desired_username.lower()
|
||||
|
||||
# == Normal User Registration == (everyone else)
|
||||
if not self.hs.config.enable_registration:
|
||||
raise SynapseError(403, "Registration has been disabled")
|
||||
|
||||
# Check if this account is upgrading from a guest account.
|
||||
guest_access_token = body.get("guest_access_token", None)
|
||||
|
||||
if "initial_device_display_name" in body and "password_hash" not in body:
|
||||
# Pull out the provided password and do basic sanity checks early.
|
||||
#
|
||||
# Note that we remove the password from the body since the auth layer
|
||||
# will store the body in the session and we don't want a plaintext
|
||||
# password store there.
|
||||
password = body.pop("password", None)
|
||||
if password is not None:
|
||||
if not isinstance(password, str) or len(password) > 512:
|
||||
raise SynapseError(400, "Invalid password")
|
||||
self.password_policy_handler.validate_password(password)
|
||||
|
||||
if "initial_device_display_name" in body and password is None:
|
||||
# ignore 'initial_device_display_name' if sent without
|
||||
# a password to work around a client bug where it sent
|
||||
# the 'initial_device_display_name' param alone, wiping out
|
||||
|
@ -484,6 +487,7 @@ class RegisterRestServlet(RestServlet):
|
|||
|
||||
session_id = self.auth_handler.get_session_id(body)
|
||||
registered_user_id = None
|
||||
password_hash = None
|
||||
if session_id:
|
||||
# if we get a registered user id out of here, it means we previously
|
||||
# registered a user for this session, so we could just return the
|
||||
|
@ -492,7 +496,12 @@ class RegisterRestServlet(RestServlet):
|
|||
registered_user_id = await self.auth_handler.get_session_data(
|
||||
session_id, "registered_user_id", None
|
||||
)
|
||||
# Extract the previously-hashed password from the session.
|
||||
password_hash = await self.auth_handler.get_session_data(
|
||||
session_id, "password_hash", None
|
||||
)
|
||||
|
||||
# Ensure that the username is valid.
|
||||
if desired_username is not None:
|
||||
await self.registration_handler.check_username(
|
||||
desired_username,
|
||||
|
@ -500,20 +509,38 @@ class RegisterRestServlet(RestServlet):
|
|||
assigned_user_id=registered_user_id,
|
||||
)
|
||||
|
||||
auth_result, params, session_id = await self.auth_handler.check_auth(
|
||||
self._registration_flows,
|
||||
request,
|
||||
body,
|
||||
self.hs.get_ip_from_request(request),
|
||||
"register a new account",
|
||||
)
|
||||
# Check if the user-interactive authentication flows are complete, if
|
||||
# not this will raise a user-interactive auth error.
|
||||
try:
|
||||
auth_result, params, session_id = await self.auth_handler.check_ui_auth(
|
||||
self._registration_flows,
|
||||
request,
|
||||
body,
|
||||
self.hs.get_ip_from_request(request),
|
||||
"register a new account",
|
||||
)
|
||||
except InteractiveAuthIncompleteError as e:
|
||||
# The user needs to provide more steps to complete auth.
|
||||
#
|
||||
# Hash the password and store it with the session since the client
|
||||
# is not required to provide the password again.
|
||||
#
|
||||
# If a password hash was previously stored we will not attempt to
|
||||
# re-hash and store it for efficiency. This assumes the password
|
||||
# does not change throughout the authentication flow, but this
|
||||
# should be fine since the data is meant to be consistent.
|
||||
if not password_hash and password:
|
||||
password_hash = await self.auth_handler.hash(password)
|
||||
await self.auth_handler.set_session_data(
|
||||
e.session_id, "password_hash", password_hash
|
||||
)
|
||||
raise
|
||||
|
||||
# Check that we're not trying to register a denied 3pid.
|
||||
#
|
||||
# the user-facing checks will probably already have happened in
|
||||
# /register/email/requestToken when we requested a 3pid, but that's not
|
||||
# guaranteed.
|
||||
|
||||
if auth_result:
|
||||
for login_type in [LoginType.EMAIL_IDENTITY, LoginType.MSISDN]:
|
||||
if login_type in auth_result:
|
||||
|
@ -535,12 +562,15 @@ class RegisterRestServlet(RestServlet):
|
|||
# don't re-register the threepids
|
||||
registered = False
|
||||
else:
|
||||
# NB: This may be from the auth handler and NOT from the POST
|
||||
assert_params_in_dict(params, ["password_hash"])
|
||||
# If we have a password in this request, prefer it. Otherwise, there
|
||||
# might be a password hash from an earlier request.
|
||||
if password:
|
||||
password_hash = await self.auth_handler.hash(password)
|
||||
if not password_hash:
|
||||
raise SynapseError(400, "Missing params: password", Codes.MISSING_PARAM)
|
||||
|
||||
desired_username = params.get("username", None)
|
||||
guest_access_token = params.get("guest_access_token", None)
|
||||
new_password_hash = params.get("password_hash", None)
|
||||
|
||||
if desired_username is not None:
|
||||
desired_username = desired_username.lower()
|
||||
|
@ -582,7 +612,7 @@ class RegisterRestServlet(RestServlet):
|
|||
|
||||
registered_user_id = await self.registration_handler.register_user(
|
||||
localpart=desired_username,
|
||||
password_hash=new_password_hash,
|
||||
password_hash=password_hash,
|
||||
guest_access_token=guest_access_token,
|
||||
threepid=threepid,
|
||||
address=client_addr,
|
||||
|
@ -595,8 +625,8 @@ class RegisterRestServlet(RestServlet):
|
|||
):
|
||||
await self.store.upsert_monthly_active_user(registered_user_id)
|
||||
|
||||
# remember that we've now registered that user account, and with
|
||||
# what user ID (since the user may not have specified)
|
||||
# Remember that the user account has been registered (and the user
|
||||
# ID it was registered with, since it might not have been specified).
|
||||
await self.auth_handler.set_session_data(
|
||||
session_id, "registered_user_id", registered_user_id
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue