Fix stuck invites

If rejecting a remote invite fails with an error response don't fail
the entire request; instead mark the invite as locally rejected.

This fixes the bug where users can get stuck invites which they can
neither accept nor reject.
pull/691/head
Erik Johnston 2016-04-05 11:13:24 +01:00
parent 0c53d750e7
commit df727f2126
4 changed files with 58 additions and 16 deletions

View File

@ -784,6 +784,7 @@ class FederationHandler(BaseHandler):
@defer.inlineCallbacks @defer.inlineCallbacks
def do_remotely_reject_invite(self, target_hosts, room_id, user_id): def do_remotely_reject_invite(self, target_hosts, room_id, user_id):
try:
origin, event = yield self._make_and_verify_event( origin, event = yield self._make_and_verify_event(
target_hosts, target_hosts,
room_id, room_id,
@ -791,6 +792,11 @@ class FederationHandler(BaseHandler):
"leave" "leave"
) )
signed_event = self._sign_event(event) signed_event = self._sign_event(event)
except SynapseError:
raise
except CodeMessageException as e:
logger.warn("Failed to reject invite: %s", e)
raise SynapseError(500, "Failed to reject invite")
# Try the host we successfully got a response to /make_join/ # Try the host we successfully got a response to /make_join/
# request first. # request first.
@ -800,10 +806,16 @@ class FederationHandler(BaseHandler):
except ValueError: except ValueError:
pass pass
try:
yield self.replication_layer.send_leave( yield self.replication_layer.send_leave(
target_hosts, target_hosts,
signed_event signed_event
) )
except SynapseError:
raise
except CodeMessageException as e:
logger.warn("Failed to reject invite: %s", e)
raise SynapseError(500, "Failed to reject invite")
context = yield self.state_handler.compute_event_context(event) context = yield self.state_handler.compute_event_context(event)

View File

@ -258,10 +258,20 @@ class RoomMemberHandler(BaseHandler):
else: else:
# send the rejection to the inviter's HS. # send the rejection to the inviter's HS.
remote_room_hosts = remote_room_hosts + [inviter.domain] remote_room_hosts = remote_room_hosts + [inviter.domain]
try:
ret = yield self.reject_remote_invite( ret = yield self.reject_remote_invite(
target.to_string(), room_id, remote_room_hosts target.to_string(), room_id, remote_room_hosts
) )
defer.returnValue(ret) defer.returnValue(ret)
except SynapseError as e:
logger.warn("Failed to reject invite: %s", e)
yield self.store.locally_reject_invite(
target.to_string(), room_id
)
defer.returnValue({})
yield self._local_membership_update( yield self._local_membership_update(
requester=requester, requester=requester,

View File

@ -94,7 +94,8 @@ class DataStore(RoomMemberStore, RoomStore,
) )
self._stream_id_gen = StreamIdGenerator( self._stream_id_gen = StreamIdGenerator(
db_conn, "events", "stream_ordering" db_conn, "events", "stream_ordering",
extra_tables=[("local_invites", "stream_id")]
) )
self._backfill_id_gen = StreamIdGenerator( self._backfill_id_gen = StreamIdGenerator(
db_conn, "events", "stream_ordering", step=-1 db_conn, "events", "stream_ordering", step=-1

View File

@ -102,6 +102,25 @@ class RoomMemberStore(SQLBaseStore):
event.state_key, event.state_key,
)) ))
@defer.inlineCallbacks
def locally_reject_invite(self, user_id, room_id):
sql = (
"UPDATE local_invites SET stream_id = ?, locally_rejected = ? WHERE"
" room_id = ? AND invitee = ? AND locally_rejected is NULL"
" AND replaced_by is NULL"
)
def f(txn, stream_ordering):
txn.execute(sql, (
stream_ordering,
True,
room_id,
user_id,
))
with self._stream_id_gen.get_next() as stream_ordering:
yield self.runInteraction("locally_reject_invite", f, stream_ordering)
def get_room_member(self, user_id, room_id): def get_room_member(self, user_id, room_id):
"""Retrieve the current state of a room member. """Retrieve the current state of a room member.