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

Commit

Permalink
Fix registration on workers (#4682)
Browse files Browse the repository at this point in the history
* Move RegistrationHandler init to HomeServer

* Move post registration actions to RegistrationHandler

* Add post regisration replication endpoint

* Newsfile
  • Loading branch information
erikjohnston authored and hawkowl committed Feb 20, 2019
1 parent c594cc8 commit dbdc565
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 151 deletions.
1 change: 1 addition & 0 deletions changelog.d/4682.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow registration and login to be handled by a worker instance.
2 changes: 0 additions & 2 deletions synapse/handlers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from .directory import DirectoryHandler
from .federation import FederationHandler
from .identity import IdentityHandler
from .register import RegistrationHandler
from .search import SearchHandler


Expand All @@ -41,7 +40,6 @@ class Handlers(object):
"""

def __init__(self, hs):
self.registration_handler = RegistrationHandler(hs)
self.federation_handler = FederationHandler(hs)
self.directory_handler = DirectoryHandler(hs)
self.admin_handler = AdminHandler(hs)
Expand Down
200 changes: 195 additions & 5 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,22 @@
from twisted.internet import defer

from synapse import types
from synapse.api.constants import LoginType
from synapse.api.errors import (
AuthError,
Codes,
InvalidCaptchaError,
RegistrationError,
SynapseError,
)
from synapse.config.server import is_threepid_reserved
from synapse.http.client import CaptchaServerHttpClient
from synapse.http.servlet import assert_params_in_dict
from synapse.replication.http.login import RegisterDeviceReplicationServlet
from synapse.replication.http.register import ReplicationRegisterServlet
from synapse.replication.http.register import (
ReplicationPostRegisterActionsServlet,
ReplicationRegisterServlet,
)
from synapse.types import RoomAlias, RoomID, UserID, create_requester
from synapse.util.async_helpers import Linearizer
from synapse.util.threepids import check_3pid_allowed
Expand All @@ -53,6 +59,7 @@ def __init__(self, hs):
self.profile_handler = hs.get_profile_handler()
self.user_directory_handler = hs.get_user_directory_handler()
self.captcha_client = CaptchaServerHttpClient(hs)
self.identity_handler = self.hs.get_handlers().identity_handler

self._next_generated_user_id = None

Expand All @@ -68,8 +75,12 @@ def __init__(self, hs):
self._register_device_client = (
RegisterDeviceReplicationServlet.make_client(hs)
)
self._post_registration_client = (
ReplicationPostRegisterActionsServlet.make_client(hs)
)
else:
self.device_handler = hs.get_device_handler()
self.pusher_pool = hs.get_pusherpool()

@defer.inlineCallbacks
def check_username(self, localpart, guest_access_token=None,
Expand Down Expand Up @@ -369,8 +380,7 @@ def register_email(self, threepidCreds):
logger.info("validating threepidcred sid %s on id server %s",
c['sid'], c['idServer'])
try:
identity_handler = self.hs.get_handlers().identity_handler
threepid = yield identity_handler.threepid_from_creds(c)
threepid = yield self.identity_handler.threepid_from_creds(c)
except Exception:
logger.exception("Couldn't validate 3pid")
raise RegistrationError(400, "Couldn't validate 3pid")
Expand All @@ -394,9 +404,8 @@ def bind_emails(self, user_id, threepidCreds):

# Now we have a matrix ID, bind it to the threepids we were given
for c in threepidCreds:
identity_handler = self.hs.get_handlers().identity_handler
# XXX: This should be a deferred list, shouldn't it?
yield identity_handler.bind_threepid(c, user_id)
yield self.identity_handler.bind_threepid(c, user_id)

def check_user_id_not_appservice_exclusive(self, user_id, allowed_appservice=None):
# don't allow people to register the server notices mxid
Expand Down Expand Up @@ -671,3 +680,184 @@ def register_device(self, user_id, device_id, initial_display_name,
)

defer.returnValue((device_id, access_token))

@defer.inlineCallbacks
def post_registration_actions(self, user_id, auth_result, access_token,
bind_email, bind_msisdn):
"""A user has completed registration
Args:
user_id (str): The user ID that consented
auth_result (dict): The authenticated credentials of the newly
registered user.
access_token (str|None): The access token of the newly logged in
device, or None if `inhibit_login` enabled.
bind_email (bool): Whether to bind the email with the identity
server
bind_msisdn (bool): Whether to bind the msisdn with the identity
server
"""
if self.hs.config.worker_app:
yield self._post_registration_client(
user_id=user_id,
auth_result=auth_result,
access_token=access_token,
bind_email=bind_email,
bind_msisdn=bind_msisdn,
)
return

if auth_result and LoginType.EMAIL_IDENTITY in auth_result:
threepid = auth_result[LoginType.EMAIL_IDENTITY]
# Necessary due to auth checks prior to the threepid being
# written to the db
if is_threepid_reserved(
self.hs.config.mau_limits_reserved_threepids, threepid
):
yield self.store.upsert_monthly_active_user(user_id)

yield self._register_email_threepid(
user_id, threepid, access_token,
bind_email,
)

if auth_result and LoginType.MSISDN in auth_result:
threepid = auth_result[LoginType.MSISDN]
yield self._register_msisdn_threepid(
user_id, threepid, bind_msisdn,
)

if auth_result and LoginType.TERMS in auth_result:
yield self._on_user_consented(
user_id, self.hs.config.user_consent_version,
)

@defer.inlineCallbacks
def _on_user_consented(self, user_id, consent_version):
"""A user consented to the terms on registration
Args:
user_id (str): The user ID that consented
consent_version (str): version of the policy the user has
consented to.
"""
logger.info("%s has consented to the privacy policy", user_id)
yield self.store.user_set_consent_version(
user_id, consent_version,
)
yield self.post_consent_actions(user_id)

@defer.inlineCallbacks
def _register_email_threepid(self, user_id, threepid, token, bind_email):
"""Add an email address as a 3pid identifier
Also adds an email pusher for the email address, if configured in the
HS config
Also optionally binds emails to the given user_id on the identity server
Must be called on master.
Args:
user_id (str): id of user
threepid (object): m.login.email.identity auth response
token (str|None): access_token for the user, or None if not logged
in.
bind_email (bool): true if the client requested the email to be
bound at the identity server
Returns:
defer.Deferred:
"""
reqd = ('medium', 'address', 'validated_at')
if any(x not in threepid for x in reqd):
# This will only happen if the ID server returns a malformed response
logger.info("Can't add incomplete 3pid")
return

yield self._auth_handler.add_threepid(
user_id,
threepid['medium'],
threepid['address'],
threepid['validated_at'],
)

# And we add an email pusher for them by default, but only
# if email notifications are enabled (so people don't start
# getting mail spam where they weren't before if email
# notifs are set up on a home server)
if (self.hs.config.email_enable_notifs and
self.hs.config.email_notif_for_new_users
and token):
# Pull the ID of the access token back out of the db
# It would really make more sense for this to be passed
# up when the access token is saved, but that's quite an
# invasive change I'd rather do separately.
user_tuple = yield self.store.get_user_by_access_token(
token
)
token_id = user_tuple["token_id"]

yield self.pusher_pool.add_pusher(
user_id=user_id,
access_token=token_id,
kind="email",
app_id="m.email",
app_display_name="Email Notifications",
device_display_name=threepid["address"],
pushkey=threepid["address"],
lang=None, # We don't know a user's language here
data={},
)

if bind_email:
logger.info("bind_email specified: binding")
logger.debug("Binding emails %s to %s" % (
threepid, user_id
))
yield self.identity_handler.bind_threepid(
threepid['threepid_creds'], user_id
)
else:
logger.info("bind_email not specified: not binding email")

@defer.inlineCallbacks
def _register_msisdn_threepid(self, user_id, threepid, bind_msisdn):
"""Add a phone number as a 3pid identifier
Also optionally binds msisdn to the given user_id on the identity server
Must be called on master.
Args:
user_id (str): id of user
threepid (object): m.login.msisdn auth response
token (str): access_token for the user
bind_email (bool): true if the client requested the email to be
bound at the identity server
Returns:
defer.Deferred:
"""
try:
assert_params_in_dict(threepid, ['medium', 'address', 'validated_at'])
except SynapseError as ex:
if ex.errcode == Codes.MISSING_PARAM:
# This will only happen if the ID server returns a malformed response
logger.info("Can't add incomplete 3pid")
defer.returnValue(None)
raise

yield self._auth_handler.add_threepid(
user_id,
threepid['medium'],
threepid['address'],
threepid['validated_at'],
)

if bind_msisdn:
logger.info("bind_msisdn specified: binding")
logger.debug("Binding msisdn %s to %s", threepid, user_id)
yield self.identity_handler.bind_threepid(
threepid['threepid_creds'], user_id
)
else:
logger.info("bind_msisdn not specified: not binding msisdn")
2 changes: 1 addition & 1 deletion synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(self, hs):

self.federation_handler = hs.get_handlers().federation_handler
self.directory_handler = hs.get_handlers().directory_handler
self.registration_handler = hs.get_handlers().registration_handler
self.registration_handler = hs.get_registration_handler()
self.profile_handler = hs.get_profile_handler()
self.event_creation_handler = hs.get_event_creation_handler()

Expand Down
2 changes: 1 addition & 1 deletion synapse/module_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def register(self, localpart):
Returns:
Deferred: a 2-tuple of (user_id, access_token)
"""
reg = self.hs.get_handlers().registration_handler
reg = self.hs.get_registration_handler()
return reg.register(localpart=localpart)

@defer.inlineCallbacks
Expand Down
2 changes: 1 addition & 1 deletion synapse/replication/http/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class RegisterDeviceReplicationServlet(ReplicationEndpoint):

def __init__(self, hs):
super(RegisterDeviceReplicationServlet, self).__init__(hs)
self.registration_handler = hs.get_handlers().registration_handler
self.registration_handler = hs.get_registration_handler()

@staticmethod
def _serialize_payload(user_id, device_id, initial_display_name, is_guest):
Expand Down
4 changes: 2 additions & 2 deletions synapse/replication/http/membership.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class ReplicationRegister3PIDGuestRestServlet(ReplicationEndpoint):
def __init__(self, hs):
super(ReplicationRegister3PIDGuestRestServlet, self).__init__(hs)

self.registeration_handler = hs.get_handlers().registration_handler
self.registeration_handler = hs.get_registration_handler()
self.store = hs.get_datastore()
self.clock = hs.get_clock()

Expand Down Expand Up @@ -251,7 +251,7 @@ class ReplicationUserJoinedLeftRoomRestServlet(ReplicationEndpoint):
def __init__(self, hs):
super(ReplicationUserJoinedLeftRoomRestServlet, self).__init__(hs)

self.registeration_handler = hs.get_handlers().registration_handler
self.registeration_handler = hs.get_registration_handler()
self.store = hs.get_datastore()
self.clock = hs.get_clock()
self.distributor = hs.get_distributor()
Expand Down
55 changes: 55 additions & 0 deletions synapse/replication/http/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,60 @@ def _handle_request(self, request, user_id):
defer.returnValue((200, {}))


class ReplicationPostRegisterActionsServlet(ReplicationEndpoint):
"""Run any post registration actions
"""

NAME = "post_register"
PATH_ARGS = ("user_id",)

def __init__(self, hs):
super(ReplicationPostRegisterActionsServlet, self).__init__(hs)
self.store = hs.get_datastore()
self.registration_handler = hs.get_registration_handler()

@staticmethod
def _serialize_payload(user_id, auth_result, access_token, bind_email,
bind_msisdn):
"""
Args:
user_id (str): The user ID that consented
auth_result (dict): The authenticated credentials of the newly
registered user.
access_token (str|None): The access token of the newly logged in
device, or None if `inhibit_login` enabled.
bind_email (bool): Whether to bind the email with the identity
server
bind_msisdn (bool): Whether to bind the msisdn with the identity
server
"""
return {
"auth_result": auth_result,
"access_token": access_token,
"bind_email": bind_email,
"bind_msisdn": bind_msisdn,
}

@defer.inlineCallbacks
def _handle_request(self, request, user_id):
content = parse_json_object_from_request(request)

auth_result = content["auth_result"]
access_token = content["access_token"]
bind_email = content["bind_email"]
bind_msisdn = content["bind_msisdn"]

yield self.registration_handler.post_registration_actions(
user_id=user_id,
auth_result=auth_result,
access_token=access_token,
bind_email=bind_email,
bind_msisdn=bind_msisdn,
)

defer.returnValue((200, {}))


def register_servlets(hs, http_server):
ReplicationRegisterServlet(hs).register(http_server)
ReplicationPostRegisterActionsServlet(hs).register(http_server)
4 changes: 2 additions & 2 deletions synapse/rest/client/v1/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __init__(self, hs):
self.jwt_algorithm = hs.config.jwt_algorithm
self.cas_enabled = hs.config.cas_enabled
self.auth_handler = self.hs.get_auth_handler()
self.registration_handler = hs.get_handlers().registration_handler
self.registration_handler = hs.get_registration_handler()
self.handlers = hs.get_handlers()
self._well_known_builder = WellKnownBuilder(hs)

Expand Down Expand Up @@ -434,7 +434,7 @@ class SSOAuthHandler(object):
def __init__(self, hs):
self._hostname = hs.hostname
self._auth_handler = hs.get_auth_handler()
self._registration_handler = hs.get_handlers().registration_handler
self._registration_handler = hs.get_registration_handler()
self._macaroon_gen = hs.get_macaroon_generator()

@defer.inlineCallbacks
Expand Down
Loading

0 comments on commit dbdc565

Please sign in to comment.