From ce1b3936826870909f237c3016367eebdcf078df Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 20 Nov 2018 13:19:37 -0700 Subject: [PATCH 1/4] Proof of concept for auto-accepting invites This is for demonstration purposes only. In practice this would actually look up the right profile and use the right thing, not to mention be in a more reasonable location. --- synapse/handlers/federation.py | 31 ++++++++++++++++++++++++++++++- synapse/handlers/room_member.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index a3bb864bb2..51741d9095 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -54,7 +54,7 @@ from synapse.replication.http.federation import ( ) from synapse.replication.http.membership import ReplicationUserJoinedLeftRoomRestServlet from synapse.state import StateResolutionStore, resolve_events_with_store -from synapse.types import UserID, get_domain_from_id +from synapse.types import UserID, get_domain_from_id, create_requester from synapse.util import logcontext, unwrapFirstError from synapse.util.async_helpers import Linearizer from synapse.util.distributor import user_joined_room @@ -1300,8 +1300,37 @@ class FederationHandler(BaseHandler): context = yield self.state_handler.compute_event_context(event) yield self.persist_events_and_notify([(event, context)]) + sender = UserID.from_string(event.sender) + target = UserID.from_string(event.state_key) + if (sender.localpart == target.localpart): + logcontext.run_in_background( + self._auto_accept_invite, + sender, target, event.room_id, + ) + defer.returnValue(event) + @defer.inlineCallbacks + def _auto_accept_invite(self, sender, target, room_id): + joined = False + for attempt in range(0, 10): + try: + yield self.hs.get_room_member_handler().update_membership( + requester=create_requester(target.to_string()), + target=target, + room_id=room_id, + action="join", + ) + joined = True + break + except Exception: + # We're going to retry, but we should log the error + logger.exception("Error auto-accepting invite on attempt %d" % attempt) + yield self.hs.get_clock().sleep(1) + if not joined: + logger.error("Giving up on trying to auto-accept invite: too many attempts") + + @defer.inlineCallbacks def do_remotely_reject_invite(self, target_hosts, room_id, user_id): origin, event = yield self._make_and_verify_event( diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index 07fd3e82fc..219ed78c33 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -29,7 +29,7 @@ import synapse.server import synapse.types from synapse.api.constants import EventTypes, Membership from synapse.api.errors import AuthError, Codes, SynapseError -from synapse.types import RoomID, UserID +from synapse.types import RoomID, UserID, RoomAlias from synapse.util.async_helpers import Linearizer from synapse.util.distributor import user_joined_room, user_left_room @@ -416,6 +416,7 @@ class RoomMemberHandler(object): ret = yield self._remote_join( requester, remote_room_hosts, room_id, target, content ) + self._send_merged_user_invites(requester, room_id) defer.returnValue(ret) elif effective_membership_state == Membership.LEAVE: @@ -450,8 +451,34 @@ class RoomMemberHandler(object): prev_events_and_hashes=prev_events_and_hashes, content=content, ) + self._send_merged_user_invites(requester, room_id) defer.returnValue(res) + @defer.inlineCallbacks + def _send_merged_user_invites(self, requester, room_id): + profile_alias = "#_profile_" + requester.user.localpart + ":" + self.hs.hostname + profile_alias = RoomAlias.from_string(profile_alias) + profile_room_id, remote_room_hosts = yield self.lookup_room_alias(profile_alias) + if profile_room_id: + linked_accounts = yield self.state_handler.get_current_state( + room_id=profile_room_id.to_string(), + event_type="m.linked_accounts", + state_key="", + ) + if not linked_accounts or not linked_accounts.content['all_children']: + return + for child_id in linked_accounts.content['all_children']: + child = UserID.from_string(child_id) + if self.hs.is_mine(child) or child_id == requester.user.to_string(): + # TODO: Handle auto-invite for local users (not a priority) + continue + self.update_membership( + requester=requester, + target=child, + room_id=room_id, + action="invite", + ) + @defer.inlineCallbacks def send_membership_event( self, From c68d5105645fca0899565857e6034972fc5cdf1a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 21 Nov 2018 13:21:21 -0700 Subject: [PATCH 2/4] Preserve log contexts in the room_member_handler --- synapse/handlers/room_member.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index 219ed78c33..18342fb675 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -30,6 +30,7 @@ import synapse.types from synapse.api.constants import EventTypes, Membership from synapse.api.errors import AuthError, Codes, SynapseError from synapse.types import RoomID, UserID, RoomAlias +from synapse.util import logcontext from synapse.util.async_helpers import Linearizer from synapse.util.distributor import user_joined_room, user_left_room @@ -416,7 +417,10 @@ class RoomMemberHandler(object): ret = yield self._remote_join( requester, remote_room_hosts, room_id, target, content ) - self._send_merged_user_invites(requester, room_id) + logcontext.run_in_background( + self._send_merged_user_invites, + requester, room_id, + ) defer.returnValue(ret) elif effective_membership_state == Membership.LEAVE: @@ -451,7 +455,10 @@ class RoomMemberHandler(object): prev_events_and_hashes=prev_events_and_hashes, content=content, ) - self._send_merged_user_invites(requester, room_id) + logcontext.run_in_background( + self._send_merged_user_invites, + requester, room_id, + ) defer.returnValue(res) @defer.inlineCallbacks From ccbf6bb222206a94948107b586551d848c7545c2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Nov 2018 08:47:35 -0700 Subject: [PATCH 3/4] Safer execution --- synapse/handlers/federation.py | 3 ++- synapse/handlers/room_member.py | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index 51741d9095..12eed15865 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -105,6 +105,7 @@ class FederationHandler(BaseHandler): self.hs = hs + self.clock = hs.get_clock() self.store = hs.get_datastore() # type: synapse.storage.DataStore self.federation_client = hs.get_federation_client() self.state_handler = hs.get_state_handler() @@ -1326,7 +1327,7 @@ class FederationHandler(BaseHandler): except Exception: # We're going to retry, but we should log the error logger.exception("Error auto-accepting invite on attempt %d" % attempt) - yield self.hs.get_clock().sleep(1) + yield self.clock.sleep(1) if not joined: logger.error("Giving up on trying to auto-accept invite: too many attempts") diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index 18342fb675..147f8e1789 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -455,10 +455,11 @@ class RoomMemberHandler(object): prev_events_and_hashes=prev_events_and_hashes, content=content, ) - logcontext.run_in_background( - self._send_merged_user_invites, - requester, room_id, - ) + if effective_membership_state == Membership.JOIN: + logcontext.run_in_background( + self._send_merged_user_invites, + requester, room_id, + ) defer.returnValue(res) @defer.inlineCallbacks @@ -479,12 +480,15 @@ class RoomMemberHandler(object): if self.hs.is_mine(child) or child_id == requester.user.to_string(): # TODO: Handle auto-invite for local users (not a priority) continue - self.update_membership( - requester=requester, - target=child, - room_id=room_id, - action="invite", - ) + try: + yield self.update_membership( + requester=requester, + target=child, + room_id=room_id, + action="invite", + ) + except Exception: + logger.exception("Failed to invite %s to %s" % (child_id, room_id)) @defer.inlineCallbacks def send_membership_event( From 921469383e73efd15a75663b71046f7692877411 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Nov 2018 08:50:05 -0700 Subject: [PATCH 4/4] Use run_as_background_process --- synapse/handlers/federation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index 12eed15865..9848534120 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -48,6 +48,7 @@ from synapse.crypto.event_signing import ( compute_event_signature, ) from synapse.events.validator import EventValidator +from synapse.metrics.background_process_metrics import run_as_background_process from synapse.replication.http.federation import ( ReplicationCleanRoomRestServlet, ReplicationFederationSendEventsRestServlet, @@ -1304,7 +1305,8 @@ class FederationHandler(BaseHandler): sender = UserID.from_string(event.sender) target = UserID.from_string(event.state_key) if (sender.localpart == target.localpart): - logcontext.run_in_background( + run_as_background_process( + "_auto_accept_invite", self._auto_accept_invite, sender, target, event.room_id, )