30
30
HttpResponseException ,
31
31
SynapseError ,
32
32
)
33
+ from synapse .config .emailconfig import ThreepidBehaviour
33
34
from synapse .util .stringutils import random_string
34
35
35
36
from ._base import BaseHandler
@@ -45,36 +46,6 @@ def __init__(self, hs):
45
46
self .federation_http_client = hs .get_http_client ()
46
47
self .hs = hs
47
48
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
-
78
49
@defer .inlineCallbacks
79
50
def threepid_from_creds (self , id_server , creds ):
80
51
"""
@@ -113,35 +84,50 @@ def threepid_from_creds(self, id_server, creds):
113
84
data = yield self .http_client .get_json (url , query_params )
114
85
except TimeoutError :
115
86
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
117
106
118
107
@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
+ ):
120
111
"""Bind a 3PID to an identity server
121
112
122
113
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
+
128
118
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
130
126
131
127
Returns:
132
128
Deferred[dict]: The response from the identity server
133
129
"""
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 )
145
131
146
132
# If an id_access_token is not supplied, force usage of v1
147
133
if id_access_token is None :
@@ -160,7 +146,6 @@ def bind_threepid(self, creds, mxid, use_v2=True):
160
146
data = yield self .http_client .post_json_get_json (
161
147
bind_url , bind_data , headers = headers
162
148
)
163
- logger .debug ("bound threepid %r to %s" , creds , mxid )
164
149
165
150
# Remember where we bound the threepid
166
151
yield self .store .add_user_bound_threepid (
@@ -182,7 +167,10 @@ def bind_threepid(self, creds, mxid, use_v2=True):
182
167
return data
183
168
184
169
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
186
174
187
175
@defer .inlineCallbacks
188
176
def try_unbind_threepid (self , mxid , threepid ):
@@ -459,6 +447,50 @@ def requestMsisdnToken(
459
447
except TimeoutError :
460
448
raise SynapseError (500 , "Timed out contacting identity server" )
461
449
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
+
462
494
463
495
def create_id_access_token_header (id_access_token ):
464
496
"""Create an Authorization header for passing to SimpleHttpClient as the header value
0 commit comments