Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 5 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
8 changes: 8 additions & 0 deletions synapse/config/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def read_config(self, config):
)
self.allow_invited_3pids = config.get("allow_invited_3pids", False)
self.registration_shared_secret = config.get("registration_shared_secret")
self.register_mxid_from_3pid = config.get("register_mxid_from_3pid")

self.bcrypt_rounds = config.get("bcrypt_rounds", 12)
self.trusted_third_party_id_servers = config["trusted_third_party_id_servers"]
Expand Down Expand Up @@ -68,6 +69,13 @@ def default_config(self, **kwargs):
# - email
# - msisdn

# Derive the user's matrix ID from a type of 3PID used when registering.
# This overrides any matrix ID the user proposes when calling /register
# The 3PID type should be present in registrations_require_3pid to avoid
# users failing to register if they don't specify the right kind of 3pid.
#
# register_mxid_from_3pid: email

# Mandate that users are only allowed to associate certain formats of
# 3PIDs with accounts on this server.
#
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def get_displayname(self, target_user):
@defer.inlineCallbacks
def set_displayname(self, target_user, requester, new_displayname, by_admin=False):
"""target_user is the user whose displayname is to be changed;
auth_user is the user attempting to make this change."""
requester is the user attempting to make this change."""
if not self.hs.is_mine(target_user):
raise SynapseError(400, "User is not hosted on this Home Server")

Expand Down
45 changes: 31 additions & 14 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ def register(
generate_token=True,
guest_access_token=None,
make_guest=False,
display_name=None,
admin=False,
):
"""Registers a new client on the server.
Expand All @@ -128,6 +129,7 @@ def register(
since it offers no means of associating a device_id with the
access_token. Instead you should call auth_handler.issue_access_token
after registration.
display_name (str): The displayname to set for this user, if any
Returns:
A tuple of (user_id, access_token).
Raises:
Expand Down Expand Up @@ -165,13 +167,20 @@ def register(
password_hash=password_hash,
was_guest=was_guest,
make_guest=make_guest,
create_profile_with_localpart=(
# If the user was a guest then they already have a profile
None if was_guest else user.localpart
),
admin=admin,
)

if display_name is not None:
display_name = (
# If the user was a guest then they already have a profile
None if was_guest else user.localpart
)

if display_name:
yield self.profile_handler.set_displayname(
user_id, user_id, display_name, by_admin=True,
)

if self.hs.config.user_directory_search_all_users:
profile = yield self.store.get_profileinfo(localpart)
yield self.user_directory_handler.handle_local_profile_change(
Expand All @@ -196,8 +205,12 @@ def register(
token=token,
password_hash=password_hash,
make_guest=make_guest,
create_profile_with_localpart=user.localpart,
)

yield self.profile_handler.set_displayname(
user_id, user_id, user.localpart, by_admin=True,
)

except SynapseError:
# if user id is taken, just generate another
user = None
Expand Down Expand Up @@ -241,8 +254,12 @@ def appservice_register(self, user_localpart, as_token):
user_id=user_id,
password_hash="",
appservice_id=service_id,
create_profile_with_localpart=user.localpart,
)

yield self.profile_handler.set_displayname(
user_id, user_id, user.localpart, by_admin=True,
)

defer.returnValue(user_id)

@defer.inlineCallbacks
Expand Down Expand Up @@ -288,7 +305,10 @@ def register_saml2(self, localpart):
user_id=user_id,
token=token,
password_hash=None,
create_profile_with_localpart=user.localpart,
)

yield self.profile_handler.set_displayname(
user_id, user_id, user.localpart, by_admin=True,
)
except Exception as e:
yield self.store.add_access_token_to_user(user_id, token)
Expand Down Expand Up @@ -443,18 +463,15 @@ def get_or_create_user(self, requester, localpart, displayname,
user_id=user_id,
token=token,
password_hash=password_hash,
create_profile_with_localpart=user.localpart,
)
if displayname is not None:
yield self.profile_handler.set_displayname(
user_id, user_id, displayname, by_admin=True,
)
else:
yield self._auth_handler.delete_access_tokens_for_user(user_id)
yield self.store.add_access_token_to_user(user_id=user_id, token=token)

if displayname is not None:
logger.info("setting user display name: %s -> %s", user_id, displayname)
yield self.profile_handler.set_displayname(
user, requester, displayname, by_admin=True,
)

defer.returnValue((user_id, token))

def auth_handler(self):
Expand Down
87 changes: 78 additions & 9 deletions synapse/rest/client/v2_alpha/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from twisted.internet import defer

import synapse
import synapse.types
from synapse import types
from synapse.api.auth import get_access_token_from_request, has_access_token
from synapse.api.constants import LoginType
from synapse.api.errors import SynapseError, Codes, UnrecognizedRequestError
Expand All @@ -31,6 +31,8 @@

import logging
import hmac
import re
from string import capwords
from hashlib import sha1
from synapse.util.async import run_on_reactor
from synapse.util.ratelimitutils import FederationRateLimiter
Expand Down Expand Up @@ -222,6 +224,8 @@ def on_POST(self, request):
raise SynapseError(400, "Invalid username")
desired_username = body['username']

desired_display_name = None

appservice = None
if has_access_token(request):
appservice = yield self.auth.get_appservice_by_req(request)
Expand Down Expand Up @@ -297,13 +301,6 @@ def on_POST(self, request):
session_id, "registered_user_id", None
)

if desired_username is not None:
yield self.registration_handler.check_username(
desired_username,
guest_access_token=guest_access_token,
assigned_user_id=registered_user_id,
)

# Only give msisdn flows if the x_show_msisdn flag is given:
# this is a hack to work around the fact that clients were shipped
# that use fallback registration if they see any flows that they don't
Expand Down Expand Up @@ -376,6 +373,69 @@ def on_POST(self, request):
Codes.THREEPID_DENIED,
)

if self.hs.config.register_mxid_from_3pid:
# override the desired_username based on the 3PID if any.
# reset it first to avoid folks picking their own username.
desired_username = None

# we should have an auth_result at this point if we're going to progress
# to register the user (i.e. we haven't picked up a registered_user_id
# from our session store), in which case get ready and gen the
# desired_username
if auth_result:
if (
(
self.hs.config.register_mxid_from_3pid == 'email' and
LoginType.EMAIL_IDENTITY in auth_result
) or (
self.hs.config.register_mxid_from_3pid == 'msisdn' and
LoginType.MSISDN in auth_result
)
):
address = auth_result[login_type]['address']
desired_username = types.strip_invalid_mxid_characters(
address.replace('@', '-').lower()
)

# find a unique mxid for the account, suffixing numbers
# if needed
while True:
try:
yield self.registration_handler.check_username(
desired_username,
guest_access_token=guest_access_token,
assigned_user_id=registered_user_id,
)
# if we got this far we passed the check.
break
except SynapseError as e:
if e.errcode == Codes.USER_IN_USE:
m = re.match(r'^(.*)(\d+)$', desired_username)
if m:
desired_username = m.group(1) + str(
int(m.group(2)) + 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a '1' on the end could be a bit confusing for msisdns

)
else:
desired_username += "1"
else:
# something else went wrong.
break

# XXX: a nasty heuristic to turn an email address into
# a displayname, as part of register_mxid_from_3pid
parts = address.replace('.', ' ').split('@')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're doing this for msisdns too - is this going to fail for these (presumably there just won't be a parts[1])

desired_display_name = (
capwords(parts[0]) +
" [" + capwords(parts[1].split(' ')[0]) + "]"
)

if desired_username is not None:
yield self.registration_handler.check_username(
desired_username,
guest_access_token=guest_access_token,
assigned_user_id=registered_user_id,
)

if registered_user_id is not None:
logger.info(
"Already registered user ID %r for this session",
Expand All @@ -390,10 +450,18 @@ def on_POST(self, request):
raise SynapseError(400, "Missing password.",
Codes.MISSING_PARAM)

desired_username = params.get("username", None)
if not self.hs.config.register_mxid_from_3pid:
desired_username = params.get("username", None)
else:
# we keep the original desired_username derived from the 3pid above
pass

new_password = params.get("password", None)
guest_access_token = params.get("guest_access_token", None)

# XXX: don't we need to validate these for length etc like we did on
# the ones from the JSON body earlier on in the method?

if desired_username is not None:
desired_username = desired_username.lower()

Expand All @@ -402,6 +470,7 @@ def on_POST(self, request):
password=new_password,
guest_access_token=guest_access_token,
generate_token=False,
display_name=desired_display_name,
)

# remember that we've now registered that user account, and with
Expand Down
14 changes: 1 addition & 13 deletions synapse/storage/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def add_access_token_to_user(self, user_id, token, device_id=None):

def register(self, user_id, token=None, password_hash=None,
was_guest=False, make_guest=False, appservice_id=None,
create_profile_with_localpart=None, admin=False):
admin=False):
"""Attempts to register an account.

Args:
Expand All @@ -142,8 +142,6 @@ def register(self, user_id, token=None, password_hash=None,
make_guest (boolean): True if the the new user should be guest,
false to add a regular user account.
appservice_id (str): The ID of the appservice registering the user.
create_profile_with_localpart (str): Optionally create a profile for
the given localpart.
Raises:
StoreError if the user_id could not be registered.
"""
Expand All @@ -156,7 +154,6 @@ def register(self, user_id, token=None, password_hash=None,
was_guest,
make_guest,
appservice_id,
create_profile_with_localpart,
admin
)

Expand All @@ -169,7 +166,6 @@ def _register(
was_guest,
make_guest,
appservice_id,
create_profile_with_localpart,
admin,
):
now = int(self.clock.time())
Expand Down Expand Up @@ -234,14 +230,6 @@ def _register(
(next_id, user_id, token,)
)

if create_profile_with_localpart:
# set a default displayname serverside to avoid ugly race
# between auto-joins and clients trying to set displaynames
txn.execute(
"INSERT INTO profiles(user_id, displayname) VALUES (?,?)",
(create_profile_with_localpart, create_profile_with_localpart)
)

self._invalidate_cache_and_stream(
txn, self.get_user_by_id, (user_id,)
)
Expand Down
12 changes: 12 additions & 0 deletions synapse/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ def contains_invalid_mxid_characters(localpart):
return any(c not in mxid_localpart_allowed_characters for c in localpart)


def strip_invalid_mxid_characters(localpart):
"""Removes any invalid characters from an mxid

Args:
localpart (basestring): the localpart to be stripped

Returns:
localpart (basestring): the localpart having been stripped
"""
return filter(lambda c: c not in mxid_localpart_allowed_characters, localpart)


class StreamToken(
namedtuple("Token", (
"room_key",
Expand Down