@@ -68,21 +68,76 @@ def _should_trust_id_server(self, id_server):
68
68
return False
69
69
return True
70
70
71
+ def _extract_items_from_creds_dict (self , creds ):
72
+ """
73
+ Retrieve entries from a "credentials" dictionary
74
+
75
+ Args:
76
+ creds (dict[str, str]): Dictionary of credentials that contain the following keys:
77
+ * client_secret|clientSecret: A unique secret str provided by the client
78
+ * id_server|idServer: the domain of the identity server to query
79
+ * id_access_token: The access token to authenticate to the identity
80
+ server with.
81
+
82
+ Returns:
83
+ tuple(str, str, str|None): A tuple containing the client_secret, the id_server,
84
+ and the id_access_token value if available.
85
+ """
86
+ client_secret = creds .get ("client_secret" ) or creds .get ("clientSecret" )
87
+ if not client_secret :
88
+ raise SynapseError (
89
+ 400 , "No client_secret in creds" , errcode = Codes .MISSING_PARAM
90
+ )
91
+
92
+ id_server = creds .get ("id_server" ) or creds .get ("idServer" )
93
+ if not id_server :
94
+ raise SynapseError (
95
+ 400 , "No id_server in creds" , errcode = Codes .MISSING_PARAM
96
+ )
97
+
98
+ id_access_token = creds .get ("id_access_token" )
99
+ return client_secret , id_server , id_access_token
100
+
71
101
@defer .inlineCallbacks
72
- def threepid_from_creds (self , creds ):
73
- if "id_server" in creds :
74
- id_server = creds ["id_server" ]
75
- elif "idServer" in creds :
76
- id_server = creds ["idServer" ]
77
- else :
78
- raise SynapseError (400 , "No id_server in creds" )
102
+ def threepid_from_creds (self , creds , use_v2 = True ):
103
+ """
104
+ Retrieve and validate a threepid identitier from a "credentials" dictionary
105
+
106
+ Args:
107
+ creds (dict[str, str]): Dictionary of credentials that contain the following keys:
108
+ * client_secret|clientSecret: A unique secret str provided by the client
109
+ * id_server|idServer: the domain of the identity server to query
110
+ * id_access_token: The access token to authenticate to the identity
111
+ server with. Required if use_v2 is true
112
+ use_v2 (bool): Whether to use v2 Identity Service API endpoints
113
+
114
+ Returns:
115
+ Deferred[dict[str,str|int]|None]: A dictionary consisting of response params to
116
+ the /getValidated3pid endpoint of the Identity Service API, or None if the
117
+ threepid was not found
118
+ """
119
+ client_secret , id_server , id_access_token = self ._extract_items_from_creds_dict (
120
+ creds
121
+ )
122
+
123
+ # If an id_access_token is not supplied, force usage of v1
124
+ if id_access_token is None :
125
+ use_v2 = False
79
126
80
- if "client_secret" in creds :
81
- client_secret = creds ["client_secret" ]
82
- elif "clientSecret" in creds :
83
- client_secret = creds ["clientSecret" ]
127
+ query_params = {"sid" : creds ["sid" ], "client_secret" : client_secret }
128
+
129
+ # Decide which API endpoint URLs and query parameters to use
130
+ if use_v2 :
131
+ url = "https://%s%s" % (
132
+ id_server ,
133
+ "/_matrix/identity/v2/3pid/getValidated3pid" ,
134
+ )
135
+ query_params ["id_access_token" ] = id_access_token
84
136
else :
85
- raise SynapseError (400 , "No client_secret in creds" )
137
+ url = "https://%s%s" % (
138
+ id_server ,
139
+ "/_matrix/identity/api/v1/3pid/getValidated3pid" ,
140
+ )
86
141
87
142
if not self ._should_trust_id_server (id_server ):
88
143
logger .warn (
@@ -95,37 +150,44 @@ def threepid_from_creds(self, creds):
95
150
if id_server in self .rewrite_identity_server_urls :
96
151
id_server = self .rewrite_identity_server_urls [id_server ]
97
152
try :
98
- data = yield self .http_client .get_json (
99
- "https://%s%s"
100
- % (id_server , "/_matrix/identity/api/v1/3pid/getValidated3pid" ),
101
- {"sid" : creds ["sid" ], "client_secret" : client_secret },
102
- )
153
+ data = yield self .http_client .get_json (url , query_params )
154
+ return data if "medium" in data else None
103
155
except HttpResponseException as e :
104
- logger .info ("getValidated3pid failed with Matrix error: %r" , e )
105
- raise e .to_synapse_error ()
156
+ if e .code != 404 or not use_v2 :
157
+ # Generic failure
158
+ logger .info ("getValidated3pid failed with Matrix error: %r" , e )
159
+ raise e .to_synapse_error ()
106
160
107
- if "medium" in data :
108
- return data
109
- return None
161
+ # This identity server is too old to understand Identity Service API v2
162
+ # Attempt v1 endpoint
163
+ logger .info ("Got 404 when POSTing JSON %s, falling back to v1 URL" , url )
164
+ return (yield self .threepid_from_creds (creds , use_v2 = False ))
110
165
111
166
@defer .inlineCallbacks
112
- def bind_threepid (self , creds , mxid ):
167
+ def bind_threepid (self , creds , mxid , use_v2 = True ):
168
+ """Bind a 3PID to an identity server
169
+
170
+ Args:
171
+ creds (dict[str, str]): Dictionary of credentials that contain the following keys:
172
+ * client_secret|clientSecret: A unique secret str provided by the client
173
+ * id_server|idServer: the domain of the identity server to query
174
+ * id_access_token: The access token to authenticate to the identity
175
+ server with. Required if use_v2 is true
176
+ mxid (str): The MXID to bind the 3PID to
177
+ use_v2 (bool): Whether to use v2 Identity Service API endpoints
178
+
179
+ Returns:
180
+ Deferred[dict]: The response from the identity server
181
+ """
113
182
logger .debug ("binding threepid %r to %s" , creds , mxid )
114
- data = None
115
183
116
- if "id_server" in creds :
117
- id_server = creds ["id_server" ]
118
- elif "idServer" in creds :
119
- id_server = creds ["idServer" ]
120
- else :
121
- raise SynapseError (400 , "No id_server in creds" )
184
+ client_secret , id_server , id_access_token = self ._extract_items_from_creds_dict (
185
+ creds
186
+ )
122
187
123
- if "client_secret" in creds :
124
- client_secret = creds ["client_secret" ]
125
- elif "clientSecret" in creds :
126
- client_secret = creds ["clientSecret" ]
127
- else :
128
- raise SynapseError (400 , "No client_secret in creds" )
188
+ # If an id_access_token is not supplied, force usage of v1
189
+ if id_access_token is None :
190
+ use_v2 = False
129
191
130
192
# if we have a rewrite rule set for the identity server,
131
193
# apply it now, but only for sending the request (not
@@ -135,11 +197,16 @@ def bind_threepid(self, creds, mxid):
135
197
else :
136
198
id_server_host = id_server
137
199
200
+ # Decide which API endpoint URLs to use
201
+ bind_data = {"sid" : creds ["sid" ], "client_secret" : client_secret , "mxid" : mxid }
202
+ if use_v2 :
203
+ bind_url = "https://%s/_matrix/identity/v2/3pid/bind" % (id_server_host ,)
204
+ bind_data ["id_access_token" ] = id_access_token
205
+ else :
206
+ bind_url = "https://%s/_matrix/identity/api/v1/3pid/bind" % (id_server_host ,)
207
+
138
208
try :
139
- data = yield self .http_client .post_json_get_json (
140
- "https://%s%s" % (id_server_host , "/_matrix/identity/api/v1/3pid/bind" ),
141
- {"sid" : creds ["sid" ], "client_secret" : client_secret , "mxid" : mxid },
142
- )
209
+ data = yield self .http_client .post_json_get_json (bind_url , bind_data )
143
210
logger .debug ("bound threepid %r to %s" , creds , mxid )
144
211
145
212
# Remember where we bound the threepid
@@ -149,9 +216,18 @@ def bind_threepid(self, creds, mxid):
149
216
address = data ["address" ],
150
217
id_server = id_server ,
151
218
)
219
+
220
+ return data
221
+ except HttpResponseException as e :
222
+ if e .code != 404 or not use_v2 :
223
+ logger .error ("3PID bind failed with Matrix error: %r" , e )
224
+ raise e .to_synapse_error ()
152
225
except CodeMessageException as e :
153
226
data = json .loads (e .msg ) # XXX WAT?
154
- return data
227
+ return data
228
+
229
+ logger .info ("Got 404 when POSTing JSON %s, falling back to v1 URL" , bind_url )
230
+ return (yield self .bind_threepid (creds , mxid , use_v2 = False ))
155
231
156
232
@defer .inlineCallbacks
157
233
def try_unbind_threepid (self , mxid , threepid ):
@@ -207,6 +283,8 @@ def try_unbind_threepid_with_id_server(self, mxid, threepid, id_server):
207
283
server doesn't support unbinding
208
284
"""
209
285
url = "https://%s/_matrix/identity/api/v1/3pid/unbind" % (id_server ,)
286
+ url_bytes = "/_matrix/identity/api/v1/3pid/unbind" .encode ("ascii" )
287
+
210
288
content = {
211
289
"mxid" : mxid ,
212
290
"threepid" : {"medium" : threepid ["medium" ], "address" : threepid ["address" ]},
@@ -218,7 +296,7 @@ def try_unbind_threepid_with_id_server(self, mxid, threepid, id_server):
218
296
auth_headers = self .federation_http_client .build_auth_headers (
219
297
destination = None ,
220
298
method = "POST" ,
221
- url_bytes = "/_matrix/identity/api/v1/3pid/unbind" . encode ( "ascii" ) ,
299
+ url_bytes = url_bytes ,
222
300
content = content ,
223
301
destination_is = id_server ,
224
302
)
0 commit comments