Allocate guest user IDs numericcally
The current random IDs are ugly and confusing when presented in UIs. This makes them prettier and easier to read. Also, disable non-automated registration of numeric IDs so that we don't need to worry so much about people carving out our automated address space and us needing to keep retrying ID registration.pull/550/head
parent
489f92e0e5
commit
79a1c0574b
|
@ -21,7 +21,6 @@ from synapse.api.errors import (
|
|||
AuthError, Codes, SynapseError, RegistrationError, InvalidCaptchaError
|
||||
)
|
||||
from ._base import BaseHandler
|
||||
import synapse.util.stringutils as stringutils
|
||||
from synapse.util.async import run_on_reactor
|
||||
from synapse.http.client import CaptchaServerHttpClient
|
||||
|
||||
|
@ -45,6 +44,8 @@ class RegistrationHandler(BaseHandler):
|
|||
self.distributor.declare("registered_user")
|
||||
self.captcha_client = CaptchaServerHttpClient(hs)
|
||||
|
||||
self._next_generated_user_id = None
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def check_username(self, localpart, guest_access_token=None):
|
||||
yield run_on_reactor()
|
||||
|
@ -91,7 +92,7 @@ class RegistrationHandler(BaseHandler):
|
|||
|
||||
Args:
|
||||
localpart : The local part of the user ID to register. If None,
|
||||
one will be randomly generated.
|
||||
one will be generated.
|
||||
password (str) : The password to assign to this user so they can
|
||||
login again. This can be None which means they cannot login again
|
||||
via a password (e.g. the user is an application service user).
|
||||
|
@ -108,6 +109,18 @@ class RegistrationHandler(BaseHandler):
|
|||
if localpart:
|
||||
yield self.check_username(localpart, guest_access_token=guest_access_token)
|
||||
|
||||
was_guest = guest_access_token is not None
|
||||
|
||||
if not was_guest:
|
||||
try:
|
||||
int(localpart)
|
||||
raise RegistrationError(
|
||||
400,
|
||||
"Numeric user IDs are reserved for guest users."
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
user = UserID(localpart, self.hs.hostname)
|
||||
user_id = user.to_string()
|
||||
|
||||
|
@ -118,40 +131,36 @@ class RegistrationHandler(BaseHandler):
|
|||
user_id=user_id,
|
||||
token=token,
|
||||
password_hash=password_hash,
|
||||
was_guest=guest_access_token is not None,
|
||||
was_guest=was_guest,
|
||||
make_guest=make_guest,
|
||||
)
|
||||
|
||||
yield registered_user(self.distributor, user)
|
||||
else:
|
||||
# autogen a random user ID
|
||||
# autogen a sequential user ID
|
||||
attempts = 0
|
||||
user_id = None
|
||||
token = None
|
||||
while not user_id:
|
||||
user = None
|
||||
while not user:
|
||||
localpart = yield self._generate_user_id(attempts > 0)
|
||||
user = UserID(localpart, self.hs.hostname)
|
||||
user_id = user.to_string()
|
||||
yield self.check_user_id_is_valid(user_id)
|
||||
if generate_token:
|
||||
token = self.auth_handler().generate_access_token(user_id)
|
||||
try:
|
||||
localpart = self._generate_user_id()
|
||||
user = UserID(localpart, self.hs.hostname)
|
||||
user_id = user.to_string()
|
||||
yield self.check_user_id_is_valid(user_id)
|
||||
if generate_token:
|
||||
token = self.auth_handler().generate_access_token(user_id)
|
||||
yield self.store.register(
|
||||
user_id=user_id,
|
||||
token=token,
|
||||
password_hash=password_hash,
|
||||
make_guest=make_guest
|
||||
)
|
||||
|
||||
yield registered_user(self.distributor, user)
|
||||
except SynapseError:
|
||||
# if user id is taken, just generate another
|
||||
user_id = None
|
||||
token = None
|
||||
attempts += 1
|
||||
if attempts > 5:
|
||||
raise RegistrationError(
|
||||
500, "Cannot generate user ID.")
|
||||
yield registered_user(self.distributor, user)
|
||||
|
||||
# We used to generate default identicons here, but nowadays
|
||||
# we want clients to generate their own as part of their branding
|
||||
|
@ -283,8 +292,16 @@ class RegistrationHandler(BaseHandler):
|
|||
errcode=Codes.EXCLUSIVE
|
||||
)
|
||||
|
||||
def _generate_user_id(self):
|
||||
return "-" + stringutils.random_string(18)
|
||||
@defer.inlineCallbacks
|
||||
def _generate_user_id(self, reseed=False):
|
||||
if reseed or self._next_generated_user_id is None:
|
||||
self._next_generated_user_id = (
|
||||
yield self.store.find_next_generated_user_id_localpart()
|
||||
)
|
||||
|
||||
id = self._next_generated_user_id
|
||||
self._next_generated_user_id += 1
|
||||
defer.returnValue(str(id))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _validate_captcha(self, ip_addr, private_key, challenge, response):
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import re
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.api.errors import StoreError, Codes
|
||||
|
@ -351,3 +353,37 @@ class RegistrationStore(SQLBaseStore):
|
|||
|
||||
ret = yield self.runInteraction("count_users", _count_users)
|
||||
defer.returnValue(ret)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def find_next_generated_user_id_localpart(self):
|
||||
"""
|
||||
Gets the localpart of the next generated user ID.
|
||||
|
||||
Generated user IDs are integers, and we aim for them to be as small as
|
||||
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.
|
||||
"""
|
||||
def _find_next_generated_user_id(txn):
|
||||
txn.execute("SELECT name FROM users")
|
||||
rows = self.cursor_to_dict(txn)
|
||||
|
||||
regex = re.compile("^@(\d+):")
|
||||
|
||||
found = set()
|
||||
|
||||
for r in rows:
|
||||
user_id = r["name"]
|
||||
match = regex.search(user_id)
|
||||
if match:
|
||||
found.add(int(match.group(1)))
|
||||
for i in xrange(len(found) + 1):
|
||||
if i not in found:
|
||||
return i
|
||||
|
||||
defer.returnValue((yield self.runInteraction(
|
||||
"find_next_generated_user_id",
|
||||
_find_next_generated_user_id
|
||||
)))
|
||||
|
|
Loading…
Reference in New Issue