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

Commit

Permalink
Don't fail requests to unbind 3pids for non supporting ID servers
Browse files Browse the repository at this point in the history
Older identity servers may not support the unbind 3pid request, so we
shouldn't fail the requests if we received one of 400/404/501. The
request still fails if we receive e.g. 500 responses, allowing clients
to retry requests on transient identity server errors that otherwise do
support the API.

Fixes #3661
  • Loading branch information
erikjohnston committed Aug 8, 2018
1 parent e5d2c67 commit 360ba89
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 20 deletions.
1 change: 1 addition & 0 deletions changelog.d/3661.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix bug on deleting 3pid when using identity servers that don't support unbind API
20 changes: 17 additions & 3 deletions synapse/handlers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,23 +828,37 @@ def add_threepid(self, user_id, medium, address, validated_at):

@defer.inlineCallbacks
def delete_threepid(self, user_id, medium, address):
"""Attempts to unbind the 3pid on the identity servers and deletes it
from the local database.
Args:
user_id (str)
medium (str)
address (str)
Returns:
Deferred[bool]: Returns True if successfully unbound the 3pid on
the identity server, False if identity server doesn't support the
unbind API.
"""

# 'Canonicalise' email addresses as per above
if medium == 'email':
address = address.lower()

identity_handler = self.hs.get_handlers().identity_handler
yield identity_handler.unbind_threepid(
result = yield identity_handler.try_unbind_threepid(
user_id,
{
'medium': medium,
'address': address,
},
)

ret = yield self.store.user_delete_threepid(
yield self.store.user_delete_threepid(
user_id, medium, address,
)
defer.returnValue(ret)
defer.returnValue(result)

def _save_session(self, session):
# TODO: Persistent storage
Expand Down
13 changes: 11 additions & 2 deletions synapse/handlers/deactivate_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ def deactivate_account(self, user_id, erase_data):
erase_data (bool): whether to GDPR-erase the user's data
Returns:
Deferred
Deferred[bool]: True if identity server supports removing
threepids, otherwise False.
"""
# FIXME: Theoretically there is a race here wherein user resets
# password using threepid.
Expand All @@ -60,16 +61,22 @@ def deactivate_account(self, user_id, erase_data):
# leave the user still active so they can try again.
# Ideally we would prevent password resets and then do this in the
# background thread.

# This will be set to false if the identity server doesn't support
# unbinding
identity_server_supports_unbinding = True

threepids = yield self.store.user_get_threepids(user_id)
for threepid in threepids:
try:
yield self._identity_handler.unbind_threepid(
result = yield self._identity_handler.try_unbind_threepid(
user_id,
{
'medium': threepid['medium'],
'address': threepid['address'],
},
)
identity_server_supports_unbinding &= result
except Exception:
# Do we want this to be a fatal error or should we carry on?
logger.exception("Failed to remove threepid from ID server")
Expand Down Expand Up @@ -103,6 +110,8 @@ def deactivate_account(self, user_id, erase_data):
# parts users from rooms (if it isn't already running)
self._start_user_parting()

defer.returnValue(identity_server_supports_unbinding)

def _start_user_parting(self):
"""
Start the process that goes through the table of users
Expand Down
30 changes: 21 additions & 9 deletions synapse/handlers/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,19 @@ def bind_threepid(self, creds, mxid):
defer.returnValue(data)

@defer.inlineCallbacks
def unbind_threepid(self, mxid, threepid):
"""
Removes a binding from an identity server
def try_unbind_threepid(self, mxid, threepid):
"""Removes a binding from an identity server
Args:
mxid (str): Matrix user ID of binding to be removed
threepid (dict): Dict with medium & address of binding to be removed
Raises:
SynapseError: If we failed to contact the identity server
Returns:
Deferred[bool]: True on success, otherwise False
Deferred[bool]: True on success, otherwise False if the identity
server doesn't support unbinding
"""
logger.debug("unbinding threepid %r from %s", threepid, mxid)
if not self.trusted_id_servers:
Expand Down Expand Up @@ -175,11 +179,19 @@ def unbind_threepid(self, mxid, threepid):
content=content,
destination_is=id_server,
)
yield self.http_client.post_json_get_json(
url,
content,
headers,
)
try:
yield self.http_client.post_json_get_json(
url,
content,
headers,
)
except HttpResponseException as e:
if e.code in (400, 404, 501,):
# The remote server probably doesn't support unbinding (yet)
defer.returnValue(False)
else:
raise SynapseError(502, "Failed to contact identity server")

defer.returnValue(True)

@defer.inlineCallbacks
Expand Down
11 changes: 9 additions & 2 deletions synapse/rest/client/v1/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,17 @@ def on_POST(self, request, target_user_id):
if not is_admin:
raise AuthError(403, "You are not a server admin")

yield self._deactivate_account_handler.deactivate_account(
result = yield self._deactivate_account_handler.deactivate_account(
target_user_id, erase,
)
defer.returnValue((200, {}))
if result:
id_server_unbind_result = "success"
else:
id_server_unbind_result = "no-support"

defer.returnValue((200, {
"id_server_unbind_result": id_server_unbind_result,
}))


class ShutdownRoomRestServlet(ClientV1RestServlet):
Expand Down
22 changes: 18 additions & 4 deletions synapse/rest/client/v2_alpha/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,17 @@ def on_POST(self, request):
yield self.auth_handler.validate_user_via_ui_auth(
requester, body, self.hs.get_ip_from_request(request),
)
yield self._deactivate_account_handler.deactivate_account(
result = yield self._deactivate_account_handler.deactivate_account(
requester.user.to_string(), erase,
)
defer.returnValue((200, {}))
if result:
id_server_unbind_result = "success"
else:
id_server_unbind_result = "no-support"

defer.returnValue((200, {
"id_server_unbind_result": id_server_unbind_result,
}))


class EmailThreepidRequestTokenRestServlet(RestServlet):
Expand Down Expand Up @@ -364,7 +371,7 @@ def on_POST(self, request):
user_id = requester.user.to_string()

try:
yield self.auth_handler.delete_threepid(
ret = yield self.auth_handler.delete_threepid(
user_id, body['medium'], body['address']
)
except Exception:
Expand All @@ -374,7 +381,14 @@ def on_POST(self, request):
logger.exception("Failed to remove threepid")
raise SynapseError(500, "Failed to remove threepid")

defer.returnValue((200, {}))
if ret:
id_server_unbind_result = "success"
else:
id_server_unbind_result = "no-support"

defer.returnValue((200, {
"id_server_unbind_result": id_server_unbind_result,
}))


class WhoamiRestServlet(RestServlet):
Expand Down

0 comments on commit 360ba89

Please sign in to comment.