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

Commit 30af161

Browse files
anoadragon453richvdh
authored andcommitted
Implement MSC2290 (#6043)
Implements MSC2290. This PR adds two new endpoints, /unstable/account/3pid/add and /unstable/account/3pid/bind. Depending on the progress of that MSC the unstable prefix may go away. This PR also removes the blacklist on some 3PID tests which occurs in #6042, as the corresponding Sytest PR changes them to use the new endpoints. Finally, it also modifies the account deactivation code such that it doesn't just try to deactivate 3PIDs that were bound to the user's account, but any 3PIDs that were bound through the homeserver on that user's account.
1 parent 1b519e0 commit 30af161

File tree

7 files changed

+203
-134
lines changed

7 files changed

+203
-134
lines changed

changelog.d/6043.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implement new Client Server API endpoints `/account/3pid/add` and `/account/3pid/bind` as per [MSC2290](https://github.com/matrix-org/matrix-doc/pull/2290).

synapse/handlers/deactivate_account.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ def deactivate_account(self, user_id, erase_data, id_server=None):
7373
# unbinding
7474
identity_server_supports_unbinding = True
7575

76-
threepids = yield self.store.user_get_threepids(user_id)
76+
# Retrieve the 3PIDs this user has bound to an identity server
77+
threepids = yield self.store.user_get_bound_threepids(user_id)
78+
7779
for threepid in threepids:
7880
try:
7981
result = yield self._identity_handler.try_unbind_threepid(

synapse/handlers/identity.py

Lines changed: 83 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
HttpResponseException,
3131
SynapseError,
3232
)
33+
from synapse.config.emailconfig import ThreepidBehaviour
3334
from synapse.util.stringutils import random_string
3435

3536
from ._base import BaseHandler
@@ -45,36 +46,6 @@ def __init__(self, hs):
4546
self.federation_http_client = hs.get_http_client()
4647
self.hs = hs
4748

48-
def _extract_items_from_creds_dict(self, creds):
49-
"""
50-
Retrieve entries from a "credentials" dictionary
51-
52-
Args:
53-
creds (dict[str, str]): Dictionary of credentials that contain the following keys:
54-
* client_secret|clientSecret: A unique secret str provided by the client
55-
* id_server|idServer: the domain of the identity server to query
56-
* id_access_token: The access token to authenticate to the identity
57-
server with.
58-
59-
Returns:
60-
tuple(str, str, str|None): A tuple containing the client_secret, the id_server,
61-
and the id_access_token value if available.
62-
"""
63-
client_secret = creds.get("client_secret") or creds.get("clientSecret")
64-
if not client_secret:
65-
raise SynapseError(
66-
400, "No client_secret in creds", errcode=Codes.MISSING_PARAM
67-
)
68-
69-
id_server = creds.get("id_server") or creds.get("idServer")
70-
if not id_server:
71-
raise SynapseError(
72-
400, "No id_server in creds", errcode=Codes.MISSING_PARAM
73-
)
74-
75-
id_access_token = creds.get("id_access_token")
76-
return client_secret, id_server, id_access_token
77-
7849
@defer.inlineCallbacks
7950
def threepid_from_creds(self, id_server, creds):
8051
"""
@@ -113,35 +84,50 @@ def threepid_from_creds(self, id_server, creds):
11384
data = yield self.http_client.get_json(url, query_params)
11485
except TimeoutError:
11586
raise SynapseError(500, "Timed out contacting identity server")
116-
return data if "medium" in data else None
87+
except HttpResponseException as e:
88+
logger.info(
89+
"%s returned %i for threepid validation for: %s",
90+
id_server,
91+
e.code,
92+
creds,
93+
)
94+
return None
95+
96+
# Old versions of Sydent return a 200 http code even on a failed validation
97+
# check. Thus, in addition to the HttpResponseException check above (which
98+
# checks for non-200 errors), we need to make sure validation_session isn't
99+
# actually an error, identified by the absence of a "medium" key
100+
# See https://github.com/matrix-org/sydent/issues/215 for details
101+
if "medium" in data:
102+
return data
103+
104+
logger.info("%s reported non-validated threepid: %s", id_server, creds)
105+
return None
117106

118107
@defer.inlineCallbacks
119-
def bind_threepid(self, creds, mxid, use_v2=True):
108+
def bind_threepid(
109+
self, client_secret, sid, mxid, id_server, id_access_token=None, use_v2=True
110+
):
120111
"""Bind a 3PID to an identity server
121112
122113
Args:
123-
creds (dict[str, str]): Dictionary of credentials that contain the following keys:
124-
* client_secret|clientSecret: A unique secret str provided by the client
125-
* id_server|idServer: the domain of the identity server to query
126-
* id_access_token: The access token to authenticate to the identity
127-
server with. Required if use_v2 is true
114+
client_secret (str): A unique secret provided by the client
115+
116+
sid (str): The ID of the validation session
117+
128118
mxid (str): The MXID to bind the 3PID to
129-
use_v2 (bool): Whether to use v2 Identity Service API endpoints
119+
120+
id_server (str): The domain of the identity server to query
121+
122+
id_access_token (str): The access token to authenticate to the identity
123+
server with, if necessary. Required if use_v2 is true
124+
125+
use_v2 (bool): Whether to use v2 Identity Service API endpoints. Defaults to True
130126
131127
Returns:
132128
Deferred[dict]: The response from the identity server
133129
"""
134-
logger.debug("binding threepid %r to %s", creds, mxid)
135-
136-
client_secret, id_server, id_access_token = self._extract_items_from_creds_dict(
137-
creds
138-
)
139-
140-
sid = creds.get("sid")
141-
if not sid:
142-
raise SynapseError(
143-
400, "No sid in three_pid_creds", errcode=Codes.MISSING_PARAM
144-
)
130+
logger.debug("Proxying threepid bind request for %s to %s", mxid, id_server)
145131

146132
# If an id_access_token is not supplied, force usage of v1
147133
if id_access_token is None:
@@ -160,7 +146,6 @@ def bind_threepid(self, creds, mxid, use_v2=True):
160146
data = yield self.http_client.post_json_get_json(
161147
bind_url, bind_data, headers=headers
162148
)
163-
logger.debug("bound threepid %r to %s", creds, mxid)
164149

165150
# Remember where we bound the threepid
166151
yield self.store.add_user_bound_threepid(
@@ -182,7 +167,10 @@ def bind_threepid(self, creds, mxid, use_v2=True):
182167
return data
183168

184169
logger.info("Got 404 when POSTing JSON %s, falling back to v1 URL", bind_url)
185-
return (yield self.bind_threepid(creds, mxid, use_v2=False))
170+
res = yield self.bind_threepid(
171+
client_secret, sid, mxid, id_server, id_access_token, use_v2=False
172+
)
173+
return res
186174

187175
@defer.inlineCallbacks
188176
def try_unbind_threepid(self, mxid, threepid):
@@ -459,6 +447,50 @@ def requestMsisdnToken(
459447
except TimeoutError:
460448
raise SynapseError(500, "Timed out contacting identity server")
461449

450+
@defer.inlineCallbacks
451+
def validate_threepid_session(self, client_secret, sid):
452+
"""Validates a threepid session with only the client secret and session ID
453+
Tries validating against any configured account_threepid_delegates as well as locally.
454+
455+
Args:
456+
client_secret (str): A secret provided by the client
457+
458+
sid (str): The ID of the session
459+
460+
Returns:
461+
Dict[str, str|int] if validation was successful, otherwise None
462+
"""
463+
# XXX: We shouldn't need to keep wrapping and unwrapping this value
464+
threepid_creds = {"client_secret": client_secret, "sid": sid}
465+
466+
# We don't actually know which medium this 3PID is. Thus we first assume it's email,
467+
# and if validation fails we try msisdn
468+
validation_session = None
469+
470+
# Try to validate as email
471+
if self.hs.config.threepid_behaviour_email == ThreepidBehaviour.REMOTE:
472+
# Ask our delegated email identity server
473+
validation_session = yield self.threepid_from_creds(
474+
self.hs.config.account_threepid_delegate_email, threepid_creds
475+
)
476+
elif self.hs.config.threepid_behaviour_email == ThreepidBehaviour.LOCAL:
477+
# Get a validated session matching these details
478+
validation_session = yield self.store.get_threepid_validation_session(
479+
"email", client_secret, sid=sid, validated=True
480+
)
481+
482+
if validation_session:
483+
return validation_session
484+
485+
# Try to validate as msisdn
486+
if self.hs.config.account_threepid_delegate_msisdn:
487+
# Ask our delegated msisdn identity server
488+
validation_session = yield self.threepid_from_creds(
489+
self.hs.config.account_threepid_delegate_msisdn, threepid_creds
490+
)
491+
492+
return validation_session
493+
462494

463495
def create_id_access_token_header(id_access_token):
464496
"""Create an Authorization header for passing to SimpleHttpClient as the header value

0 commit comments

Comments
 (0)