|
@@ -61,21 +61,76 @@ class IdentityHandler(BaseHandler):
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
+ def _extract_items_from_creds_dict(self, creds):
|
|
|
+ """
|
|
|
+ Retrieve entries from a "credentials" dictionary
|
|
|
+
|
|
|
+ Args:
|
|
|
+ creds (dict[str, str]): Dictionary of credentials that contain the following keys:
|
|
|
+ * client_secret|clientSecret: A unique secret str provided by the client
|
|
|
+ * id_server|idServer: the domain of the identity server to query
|
|
|
+ * id_access_token: The access token to authenticate to the identity
|
|
|
+ server with.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ tuple(str, str, str|None): A tuple containing the client_secret, the id_server,
|
|
|
+ and the id_access_token value if available.
|
|
|
+ """
|
|
|
+ client_secret = creds.get("client_secret") or creds.get("clientSecret")
|
|
|
+ if not client_secret:
|
|
|
+ raise SynapseError(
|
|
|
+ 400, "No client_secret in creds", errcode=Codes.MISSING_PARAM
|
|
|
+ )
|
|
|
+
|
|
|
+ id_server = creds.get("id_server") or creds.get("idServer")
|
|
|
+ if not id_server:
|
|
|
+ raise SynapseError(
|
|
|
+ 400, "No id_server in creds", errcode=Codes.MISSING_PARAM
|
|
|
+ )
|
|
|
+
|
|
|
+ id_access_token = creds.get("id_access_token")
|
|
|
+ return client_secret, id_server, id_access_token
|
|
|
+
|
|
|
@defer.inlineCallbacks
|
|
|
- def threepid_from_creds(self, creds):
|
|
|
- if "id_server" in creds:
|
|
|
- id_server = creds["id_server"]
|
|
|
- elif "idServer" in creds:
|
|
|
- id_server = creds["idServer"]
|
|
|
- else:
|
|
|
- raise SynapseError(400, "No id_server in creds")
|
|
|
+ def threepid_from_creds(self, creds, use_v2=True):
|
|
|
+ """
|
|
|
+ Retrieve and validate a threepid identitier from a "credentials" dictionary
|
|
|
+
|
|
|
+ Args:
|
|
|
+ creds (dict[str, str]): Dictionary of credentials that contain the following keys:
|
|
|
+ * client_secret|clientSecret: A unique secret str provided by the client
|
|
|
+ * id_server|idServer: the domain of the identity server to query
|
|
|
+ * id_access_token: The access token to authenticate to the identity
|
|
|
+ server with. Required if use_v2 is true
|
|
|
+ use_v2 (bool): Whether to use v2 Identity Service API endpoints
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Deferred[dict[str,str|int]|None]: A dictionary consisting of response params to
|
|
|
+ the /getValidated3pid endpoint of the Identity Service API, or None if the
|
|
|
+ threepid was not found
|
|
|
+ """
|
|
|
+ client_secret, id_server, id_access_token = self._extract_items_from_creds_dict(
|
|
|
+ creds
|
|
|
+ )
|
|
|
|
|
|
- if "client_secret" in creds:
|
|
|
- client_secret = creds["client_secret"]
|
|
|
- elif "clientSecret" in creds:
|
|
|
- client_secret = creds["clientSecret"]
|
|
|
+ # If an id_access_token is not supplied, force usage of v1
|
|
|
+ if id_access_token is None:
|
|
|
+ use_v2 = False
|
|
|
+
|
|
|
+ query_params = {"sid": creds["sid"], "client_secret": client_secret}
|
|
|
+
|
|
|
+ # Decide which API endpoint URLs and query parameters to use
|
|
|
+ if use_v2:
|
|
|
+ url = "https://%s%s" % (
|
|
|
+ id_server,
|
|
|
+ "/_matrix/identity/v2/3pid/getValidated3pid",
|
|
|
+ )
|
|
|
+ query_params["id_access_token"] = id_access_token
|
|
|
else:
|
|
|
- raise SynapseError(400, "No client_secret in creds")
|
|
|
+ url = "https://%s%s" % (
|
|
|
+ id_server,
|
|
|
+ "/_matrix/identity/api/v1/3pid/getValidated3pid",
|
|
|
+ )
|
|
|
|
|
|
if not self._should_trust_id_server(id_server):
|
|
|
logger.warn(
|
|
@@ -85,43 +140,55 @@ class IdentityHandler(BaseHandler):
|
|
|
return None
|
|
|
|
|
|
try:
|
|
|
- data = yield self.http_client.get_json(
|
|
|
- "https://%s%s"
|
|
|
- % (id_server, "/_matrix/identity/api/v1/3pid/getValidated3pid"),
|
|
|
- {"sid": creds["sid"], "client_secret": client_secret},
|
|
|
- )
|
|
|
+ data = yield self.http_client.get_json(url, query_params)
|
|
|
+ return data if "medium" in data else None
|
|
|
except HttpResponseException as e:
|
|
|
- logger.info("getValidated3pid failed with Matrix error: %r", e)
|
|
|
- raise e.to_synapse_error()
|
|
|
+ if e.code != 404 or not use_v2:
|
|
|
+ # Generic failure
|
|
|
+ logger.info("getValidated3pid failed with Matrix error: %r", e)
|
|
|
+ raise e.to_synapse_error()
|
|
|
|
|
|
- if "medium" in data:
|
|
|
- return data
|
|
|
- return None
|
|
|
+ # This identity server is too old to understand Identity Service API v2
|
|
|
+ # Attempt v1 endpoint
|
|
|
+ logger.info("Got 404 when POSTing JSON %s, falling back to v1 URL", url)
|
|
|
+ return (yield self.threepid_from_creds(creds, use_v2=False))
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
- def bind_threepid(self, creds, mxid):
|
|
|
+ def bind_threepid(self, creds, mxid, use_v2=True):
|
|
|
+ """Bind a 3PID to an identity server
|
|
|
+
|
|
|
+ Args:
|
|
|
+ creds (dict[str, str]): Dictionary of credentials that contain the following keys:
|
|
|
+ * client_secret|clientSecret: A unique secret str provided by the client
|
|
|
+ * id_server|idServer: the domain of the identity server to query
|
|
|
+ * id_access_token: The access token to authenticate to the identity
|
|
|
+ server with. Required if use_v2 is true
|
|
|
+ mxid (str): The MXID to bind the 3PID to
|
|
|
+ use_v2 (bool): Whether to use v2 Identity Service API endpoints
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Deferred[dict]: The response from the identity server
|
|
|
+ """
|
|
|
logger.debug("binding threepid %r to %s", creds, mxid)
|
|
|
- data = None
|
|
|
|
|
|
- if "id_server" in creds:
|
|
|
- id_server = creds["id_server"]
|
|
|
- elif "idServer" in creds:
|
|
|
- id_server = creds["idServer"]
|
|
|
- else:
|
|
|
- raise SynapseError(400, "No id_server in creds")
|
|
|
+ client_secret, id_server, id_access_token = self._extract_items_from_creds_dict(
|
|
|
+ creds
|
|
|
+ )
|
|
|
+
|
|
|
+ # If an id_access_token is not supplied, force usage of v1
|
|
|
+ if id_access_token is None:
|
|
|
+ use_v2 = False
|
|
|
|
|
|
- if "client_secret" in creds:
|
|
|
- client_secret = creds["client_secret"]
|
|
|
- elif "clientSecret" in creds:
|
|
|
- client_secret = creds["clientSecret"]
|
|
|
+ # Decide which API endpoint URLs to use
|
|
|
+ bind_data = {"sid": creds["sid"], "client_secret": client_secret, "mxid": mxid}
|
|
|
+ if use_v2:
|
|
|
+ bind_url = "https://%s/_matrix/identity/v2/3pid/bind" % (id_server,)
|
|
|
+ bind_data["id_access_token"] = id_access_token
|
|
|
else:
|
|
|
- raise SynapseError(400, "No client_secret in creds")
|
|
|
+ bind_url = "https://%s/_matrix/identity/api/v1/3pid/bind" % (id_server,)
|
|
|
|
|
|
try:
|
|
|
- data = yield self.http_client.post_json_get_json(
|
|
|
- "https://%s%s" % (id_server, "/_matrix/identity/api/v1/3pid/bind"),
|
|
|
- {"sid": creds["sid"], "client_secret": client_secret, "mxid": mxid},
|
|
|
- )
|
|
|
+ data = yield self.http_client.post_json_get_json(bind_url, bind_data)
|
|
|
logger.debug("bound threepid %r to %s", creds, mxid)
|
|
|
|
|
|
# Remember where we bound the threepid
|
|
@@ -131,9 +198,18 @@ class IdentityHandler(BaseHandler):
|
|
|
address=data["address"],
|
|
|
id_server=id_server,
|
|
|
)
|
|
|
+
|
|
|
+ return data
|
|
|
+ except HttpResponseException as e:
|
|
|
+ if e.code != 404 or not use_v2:
|
|
|
+ logger.error("3PID bind failed with Matrix error: %r", e)
|
|
|
+ raise e.to_synapse_error()
|
|
|
except CodeMessageException as e:
|
|
|
data = json.loads(e.msg) # XXX WAT?
|
|
|
- return data
|
|
|
+ return data
|
|
|
+
|
|
|
+ logger.info("Got 404 when POSTing JSON %s, falling back to v1 URL", bind_url)
|
|
|
+ return (yield self.bind_threepid(creds, mxid, use_v2=False))
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
def try_unbind_threepid(self, mxid, threepid):
|
|
@@ -189,6 +265,8 @@ class IdentityHandler(BaseHandler):
|
|
|
server doesn't support unbinding
|
|
|
"""
|
|
|
url = "https://%s/_matrix/identity/api/v1/3pid/unbind" % (id_server,)
|
|
|
+ url_bytes = "/_matrix/identity/api/v1/3pid/unbind".encode("ascii")
|
|
|
+
|
|
|
content = {
|
|
|
"mxid": mxid,
|
|
|
"threepid": {"medium": threepid["medium"], "address": threepid["address"]},
|
|
@@ -200,7 +278,7 @@ class IdentityHandler(BaseHandler):
|
|
|
auth_headers = self.federation_http_client.build_auth_headers(
|
|
|
destination=None,
|
|
|
method="POST",
|
|
|
- url_bytes="/_matrix/identity/api/v1/3pid/unbind".encode("ascii"),
|
|
|
+ url_bytes=url_bytes,
|
|
|
content=content,
|
|
|
destination_is=id_server,
|
|
|
)
|