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

Commit dbdc565

Browse files
erikjohnstonhawkowl
authored andcommitted
Fix registration on workers (#4682)
* Move RegistrationHandler init to HomeServer * Move post registration actions to RegistrationHandler * Add post regisration replication endpoint * Newsfile
1 parent c594cc8 commit dbdc565

File tree

14 files changed

+277
-151
lines changed

14 files changed

+277
-151
lines changed

changelog.d/4682.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow registration and login to be handled by a worker instance.

synapse/handlers/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from .directory import DirectoryHandler
1818
from .federation import FederationHandler
1919
from .identity import IdentityHandler
20-
from .register import RegistrationHandler
2120
from .search import SearchHandler
2221

2322

@@ -41,7 +40,6 @@ class Handlers(object):
4140
"""
4241

4342
def __init__(self, hs):
44-
self.registration_handler = RegistrationHandler(hs)
4543
self.federation_handler = FederationHandler(hs)
4644
self.directory_handler = DirectoryHandler(hs)
4745
self.admin_handler = AdminHandler(hs)

synapse/handlers/register.py

+195-5
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,22 @@
1919
from twisted.internet import defer
2020

2121
from synapse import types
22+
from synapse.api.constants import LoginType
2223
from synapse.api.errors import (
2324
AuthError,
2425
Codes,
2526
InvalidCaptchaError,
2627
RegistrationError,
2728
SynapseError,
2829
)
30+
from synapse.config.server import is_threepid_reserved
2931
from synapse.http.client import CaptchaServerHttpClient
32+
from synapse.http.servlet import assert_params_in_dict
3033
from synapse.replication.http.login import RegisterDeviceReplicationServlet
31-
from synapse.replication.http.register import ReplicationRegisterServlet
34+
from synapse.replication.http.register import (
35+
ReplicationPostRegisterActionsServlet,
36+
ReplicationRegisterServlet,
37+
)
3238
from synapse.types import RoomAlias, RoomID, UserID, create_requester
3339
from synapse.util.async_helpers import Linearizer
3440
from synapse.util.threepids import check_3pid_allowed
@@ -53,6 +59,7 @@ def __init__(self, hs):
5359
self.profile_handler = hs.get_profile_handler()
5460
self.user_directory_handler = hs.get_user_directory_handler()
5561
self.captcha_client = CaptchaServerHttpClient(hs)
62+
self.identity_handler = self.hs.get_handlers().identity_handler
5663

5764
self._next_generated_user_id = None
5865

@@ -68,8 +75,12 @@ def __init__(self, hs):
6875
self._register_device_client = (
6976
RegisterDeviceReplicationServlet.make_client(hs)
7077
)
78+
self._post_registration_client = (
79+
ReplicationPostRegisterActionsServlet.make_client(hs)
80+
)
7181
else:
7282
self.device_handler = hs.get_device_handler()
83+
self.pusher_pool = hs.get_pusherpool()
7384

7485
@defer.inlineCallbacks
7586
def check_username(self, localpart, guest_access_token=None,
@@ -369,8 +380,7 @@ def register_email(self, threepidCreds):
369380
logger.info("validating threepidcred sid %s on id server %s",
370381
c['sid'], c['idServer'])
371382
try:
372-
identity_handler = self.hs.get_handlers().identity_handler
373-
threepid = yield identity_handler.threepid_from_creds(c)
383+
threepid = yield self.identity_handler.threepid_from_creds(c)
374384
except Exception:
375385
logger.exception("Couldn't validate 3pid")
376386
raise RegistrationError(400, "Couldn't validate 3pid")
@@ -394,9 +404,8 @@ def bind_emails(self, user_id, threepidCreds):
394404

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

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

673682
defer.returnValue((device_id, access_token))
683+
684+
@defer.inlineCallbacks
685+
def post_registration_actions(self, user_id, auth_result, access_token,
686+
bind_email, bind_msisdn):
687+
"""A user has completed registration
688+
689+
Args:
690+
user_id (str): The user ID that consented
691+
auth_result (dict): The authenticated credentials of the newly
692+
registered user.
693+
access_token (str|None): The access token of the newly logged in
694+
device, or None if `inhibit_login` enabled.
695+
bind_email (bool): Whether to bind the email with the identity
696+
server
697+
bind_msisdn (bool): Whether to bind the msisdn with the identity
698+
server
699+
"""
700+
if self.hs.config.worker_app:
701+
yield self._post_registration_client(
702+
user_id=user_id,
703+
auth_result=auth_result,
704+
access_token=access_token,
705+
bind_email=bind_email,
706+
bind_msisdn=bind_msisdn,
707+
)
708+
return
709+
710+
if auth_result and LoginType.EMAIL_IDENTITY in auth_result:
711+
threepid = auth_result[LoginType.EMAIL_IDENTITY]
712+
# Necessary due to auth checks prior to the threepid being
713+
# written to the db
714+
if is_threepid_reserved(
715+
self.hs.config.mau_limits_reserved_threepids, threepid
716+
):
717+
yield self.store.upsert_monthly_active_user(user_id)
718+
719+
yield self._register_email_threepid(
720+
user_id, threepid, access_token,
721+
bind_email,
722+
)
723+
724+
if auth_result and LoginType.MSISDN in auth_result:
725+
threepid = auth_result[LoginType.MSISDN]
726+
yield self._register_msisdn_threepid(
727+
user_id, threepid, bind_msisdn,
728+
)
729+
730+
if auth_result and LoginType.TERMS in auth_result:
731+
yield self._on_user_consented(
732+
user_id, self.hs.config.user_consent_version,
733+
)
734+
735+
@defer.inlineCallbacks
736+
def _on_user_consented(self, user_id, consent_version):
737+
"""A user consented to the terms on registration
738+
739+
Args:
740+
user_id (str): The user ID that consented
741+
consent_version (str): version of the policy the user has
742+
consented to.
743+
"""
744+
logger.info("%s has consented to the privacy policy", user_id)
745+
yield self.store.user_set_consent_version(
746+
user_id, consent_version,
747+
)
748+
yield self.post_consent_actions(user_id)
749+
750+
@defer.inlineCallbacks
751+
def _register_email_threepid(self, user_id, threepid, token, bind_email):
752+
"""Add an email address as a 3pid identifier
753+
754+
Also adds an email pusher for the email address, if configured in the
755+
HS config
756+
757+
Also optionally binds emails to the given user_id on the identity server
758+
759+
Must be called on master.
760+
761+
Args:
762+
user_id (str): id of user
763+
threepid (object): m.login.email.identity auth response
764+
token (str|None): access_token for the user, or None if not logged
765+
in.
766+
bind_email (bool): true if the client requested the email to be
767+
bound at the identity server
768+
Returns:
769+
defer.Deferred:
770+
"""
771+
reqd = ('medium', 'address', 'validated_at')
772+
if any(x not in threepid for x in reqd):
773+
# This will only happen if the ID server returns a malformed response
774+
logger.info("Can't add incomplete 3pid")
775+
return
776+
777+
yield self._auth_handler.add_threepid(
778+
user_id,
779+
threepid['medium'],
780+
threepid['address'],
781+
threepid['validated_at'],
782+
)
783+
784+
# And we add an email pusher for them by default, but only
785+
# if email notifications are enabled (so people don't start
786+
# getting mail spam where they weren't before if email
787+
# notifs are set up on a home server)
788+
if (self.hs.config.email_enable_notifs and
789+
self.hs.config.email_notif_for_new_users
790+
and token):
791+
# Pull the ID of the access token back out of the db
792+
# It would really make more sense for this to be passed
793+
# up when the access token is saved, but that's quite an
794+
# invasive change I'd rather do separately.
795+
user_tuple = yield self.store.get_user_by_access_token(
796+
token
797+
)
798+
token_id = user_tuple["token_id"]
799+
800+
yield self.pusher_pool.add_pusher(
801+
user_id=user_id,
802+
access_token=token_id,
803+
kind="email",
804+
app_id="m.email",
805+
app_display_name="Email Notifications",
806+
device_display_name=threepid["address"],
807+
pushkey=threepid["address"],
808+
lang=None, # We don't know a user's language here
809+
data={},
810+
)
811+
812+
if bind_email:
813+
logger.info("bind_email specified: binding")
814+
logger.debug("Binding emails %s to %s" % (
815+
threepid, user_id
816+
))
817+
yield self.identity_handler.bind_threepid(
818+
threepid['threepid_creds'], user_id
819+
)
820+
else:
821+
logger.info("bind_email not specified: not binding email")
822+
823+
@defer.inlineCallbacks
824+
def _register_msisdn_threepid(self, user_id, threepid, bind_msisdn):
825+
"""Add a phone number as a 3pid identifier
826+
827+
Also optionally binds msisdn to the given user_id on the identity server
828+
829+
Must be called on master.
830+
831+
Args:
832+
user_id (str): id of user
833+
threepid (object): m.login.msisdn auth response
834+
token (str): access_token for the user
835+
bind_email (bool): true if the client requested the email to be
836+
bound at the identity server
837+
Returns:
838+
defer.Deferred:
839+
"""
840+
try:
841+
assert_params_in_dict(threepid, ['medium', 'address', 'validated_at'])
842+
except SynapseError as ex:
843+
if ex.errcode == Codes.MISSING_PARAM:
844+
# This will only happen if the ID server returns a malformed response
845+
logger.info("Can't add incomplete 3pid")
846+
defer.returnValue(None)
847+
raise
848+
849+
yield self._auth_handler.add_threepid(
850+
user_id,
851+
threepid['medium'],
852+
threepid['address'],
853+
threepid['validated_at'],
854+
)
855+
856+
if bind_msisdn:
857+
logger.info("bind_msisdn specified: binding")
858+
logger.debug("Binding msisdn %s to %s", threepid, user_id)
859+
yield self.identity_handler.bind_threepid(
860+
threepid['threepid_creds'], user_id
861+
)
862+
else:
863+
logger.info("bind_msisdn not specified: not binding msisdn")

synapse/handlers/room_member.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def __init__(self, hs):
6161

6262
self.federation_handler = hs.get_handlers().federation_handler
6363
self.directory_handler = hs.get_handlers().directory_handler
64-
self.registration_handler = hs.get_handlers().registration_handler
64+
self.registration_handler = hs.get_registration_handler()
6565
self.profile_handler = hs.get_profile_handler()
6666
self.event_creation_handler = hs.get_event_creation_handler()
6767

synapse/module_api/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def register(self, localpart):
7979
Returns:
8080
Deferred: a 2-tuple of (user_id, access_token)
8181
"""
82-
reg = self.hs.get_handlers().registration_handler
82+
reg = self.hs.get_registration_handler()
8383
return reg.register(localpart=localpart)
8484

8585
@defer.inlineCallbacks

synapse/replication/http/login.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class RegisterDeviceReplicationServlet(ReplicationEndpoint):
3535

3636
def __init__(self, hs):
3737
super(RegisterDeviceReplicationServlet, self).__init__(hs)
38-
self.registration_handler = hs.get_handlers().registration_handler
38+
self.registration_handler = hs.get_registration_handler()
3939

4040
@staticmethod
4141
def _serialize_payload(user_id, device_id, initial_display_name, is_guest):

synapse/replication/http/membership.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class ReplicationRegister3PIDGuestRestServlet(ReplicationEndpoint):
191191
def __init__(self, hs):
192192
super(ReplicationRegister3PIDGuestRestServlet, self).__init__(hs)
193193

194-
self.registeration_handler = hs.get_handlers().registration_handler
194+
self.registeration_handler = hs.get_registration_handler()
195195
self.store = hs.get_datastore()
196196
self.clock = hs.get_clock()
197197

@@ -251,7 +251,7 @@ class ReplicationUserJoinedLeftRoomRestServlet(ReplicationEndpoint):
251251
def __init__(self, hs):
252252
super(ReplicationUserJoinedLeftRoomRestServlet, self).__init__(hs)
253253

254-
self.registeration_handler = hs.get_handlers().registration_handler
254+
self.registeration_handler = hs.get_registration_handler()
255255
self.store = hs.get_datastore()
256256
self.clock = hs.get_clock()
257257
self.distributor = hs.get_distributor()

synapse/replication/http/register.py

+55
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,60 @@ def _handle_request(self, request, user_id):
8787
defer.returnValue((200, {}))
8888

8989

90+
class ReplicationPostRegisterActionsServlet(ReplicationEndpoint):
91+
"""Run any post registration actions
92+
"""
93+
94+
NAME = "post_register"
95+
PATH_ARGS = ("user_id",)
96+
97+
def __init__(self, hs):
98+
super(ReplicationPostRegisterActionsServlet, self).__init__(hs)
99+
self.store = hs.get_datastore()
100+
self.registration_handler = hs.get_registration_handler()
101+
102+
@staticmethod
103+
def _serialize_payload(user_id, auth_result, access_token, bind_email,
104+
bind_msisdn):
105+
"""
106+
Args:
107+
user_id (str): The user ID that consented
108+
auth_result (dict): The authenticated credentials of the newly
109+
registered user.
110+
access_token (str|None): The access token of the newly logged in
111+
device, or None if `inhibit_login` enabled.
112+
bind_email (bool): Whether to bind the email with the identity
113+
server
114+
bind_msisdn (bool): Whether to bind the msisdn with the identity
115+
server
116+
"""
117+
return {
118+
"auth_result": auth_result,
119+
"access_token": access_token,
120+
"bind_email": bind_email,
121+
"bind_msisdn": bind_msisdn,
122+
}
123+
124+
@defer.inlineCallbacks
125+
def _handle_request(self, request, user_id):
126+
content = parse_json_object_from_request(request)
127+
128+
auth_result = content["auth_result"]
129+
access_token = content["access_token"]
130+
bind_email = content["bind_email"]
131+
bind_msisdn = content["bind_msisdn"]
132+
133+
yield self.registration_handler.post_registration_actions(
134+
user_id=user_id,
135+
auth_result=auth_result,
136+
access_token=access_token,
137+
bind_email=bind_email,
138+
bind_msisdn=bind_msisdn,
139+
)
140+
141+
defer.returnValue((200, {}))
142+
143+
90144
def register_servlets(hs, http_server):
91145
ReplicationRegisterServlet(hs).register(http_server)
146+
ReplicationPostRegisterActionsServlet(hs).register(http_server)

synapse/rest/client/v1/login.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def __init__(self, hs):
9494
self.jwt_algorithm = hs.config.jwt_algorithm
9595
self.cas_enabled = hs.config.cas_enabled
9696
self.auth_handler = self.hs.get_auth_handler()
97-
self.registration_handler = hs.get_handlers().registration_handler
97+
self.registration_handler = hs.get_registration_handler()
9898
self.handlers = hs.get_handlers()
9999
self._well_known_builder = WellKnownBuilder(hs)
100100

@@ -434,7 +434,7 @@ class SSOAuthHandler(object):
434434
def __init__(self, hs):
435435
self._hostname = hs.hostname
436436
self._auth_handler = hs.get_auth_handler()
437-
self._registration_handler = hs.get_handlers().registration_handler
437+
self._registration_handler = hs.get_registration_handler()
438438
self._macaroon_gen = hs.get_macaroon_generator()
439439

440440
@defer.inlineCallbacks

0 commit comments

Comments
 (0)