Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Make numeric user_id checker start at @0, and don't ratelimit on checking #6338

Merged
merged 5 commits into from
Nov 6, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/6338.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prevent the server taking a long time to start up when guest registration is enabled.
51 changes: 30 additions & 21 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
AuthError,
Codes,
ConsentNotGivenError,
LimitExceededError,
RegistrationError,
SynapseError,
)
Expand Down Expand Up @@ -168,6 +167,7 @@ def register_user(
Raises:
RegistrationError if there was a problem registering.
"""
yield self.check_registration_ratelimit(address)

yield self.auth.check_auth_blocking(threepid=threepid)
password_hash = None
Expand Down Expand Up @@ -217,8 +217,8 @@ def register_user(

else:
# autogen a sequential user ID
user = None
while not user:
# Fail after being unable to find a suitable ID a few times
for x in range(10):
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
localpart = yield self._generate_user_id()
user = UserID(localpart, self.hs.hostname)
user_id = user.to_string()
Expand All @@ -233,10 +233,12 @@ def register_user(
create_profile_with_displayname=default_display_name,
address=address,
)

# Successfully registered
break
except SynapseError:
# if user id is taken, just generate another
user = None
user_id = None
pass

if not self.hs.config.user_consent_at_registration:
yield self._auto_join_rooms(user_id)
Expand Down Expand Up @@ -414,6 +416,29 @@ def _join_user_to_room(self, requester, room_identifier):
ratelimit=False,
)

def check_registration_ratelimit(self, address):
"""A simple helper method to check whether the registration rate limit has been hit
for a given IP address

Args:
address (str|None): the IP address used to perform the registration. If this is
None, no ratelimiting will be performed.

Raises:
LimitExceededError: If the rate limit has been exceeded.
"""
if not address:
return

time_now = self.clock.time()
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

self.ratelimiter.ratelimit(
address,
time_now_s=time_now,
rate_hz=self.hs.config.rc_registration.per_second,
burst_count=self.hs.config.rc_registration.burst_count,
)

def register_with_store(
self,
user_id,
Expand Down Expand Up @@ -446,22 +471,6 @@ def register_with_store(
Returns:
Deferred
"""
# Don't rate limit for app services
if appservice_id is None and address is not None:
time_now = self.clock.time()

allowed, time_allowed = self.ratelimiter.can_do_action(
address,
time_now_s=time_now,
rate_hz=self.hs.config.rc_registration.per_second,
burst_count=self.hs.config.rc_registration.burst_count,
)

if not allowed:
raise LimitExceededError(
retry_after_ms=int(1000 * (time_allowed - time_now))
)

if self.hs.config.worker_app:
return self._register_client(
user_id=user_id,
Expand Down
2 changes: 2 additions & 0 deletions synapse/replication/http/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ def _serialize_payload(
async def _handle_request(self, request, user_id):
content = parse_json_object_from_request(request)

self.registration_handler.check_registration_ratelimit(content["address"])

await self.registration_handler.register_with_store(
user_id=user_id,
password_hash=content["password_hash"],
Expand Down
8 changes: 4 additions & 4 deletions synapse/storage/data_stores/main/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,14 +488,14 @@ def find_next_generated_user_id_localpart(self):
we can. Unfortunately, it's possible some of them are already taken by
existing users, and there may be gaps in the already taken range. This
function returns the start of the first allocatable gap. This is to
avoid the case of ID 10000000 being pre-allocated, so us wasting the
first (and shortest) many generated user IDs.
avoid the case of ID 1000 being pre-allocated and starting at 1001 while
0-999 are available.
"""

def _find_next_generated_user_id(txn):
# We bound between '@1' and '@a' to avoid pulling the entire table
# We bound between '@0' and '@a' to avoid pulling the entire table
# out.
txn.execute("SELECT name FROM users WHERE '@1' <= name AND name < '@a'")
txn.execute("SELECT name FROM users WHERE '@0' <= name AND name < '@a'")

regex = re.compile(r"^@(\d+):")

Expand Down