This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "Revert moving lookup stuff to IdentityHandler"
This reverts commit 7f647bc.
- Loading branch information
1 parent
7f647bc
commit ee499bf
Showing
2 changed files
with
194 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,15 +20,20 @@ | |
import logging | ||
|
||
from canonicaljson import json | ||
from signedjson.key import decode_verify_key_bytes | ||
from signedjson.sign import verify_signed_json | ||
from unpaddedbase64 import decode_base64 | ||
|
||
from twisted.internet import defer | ||
|
||
from synapse.api.errors import ( | ||
AuthError, | ||
CodeMessageException, | ||
Codes, | ||
HttpResponseException, | ||
SynapseError, | ||
) | ||
from synapse.util.hash import sha256_and_url_safe_base64 | ||
|
||
from ._base import BaseHandler | ||
|
||
|
@@ -283,6 +288,193 @@ def requestMsisdnToken( | |
logger.info("Proxied requestToken failed: %r", e) | ||
raise e.to_synapse_error() | ||
|
||
@defer.inlineCallbacks | ||
def lookup_3pid(self, id_server, medium, address, id_access_token=None): | ||
"""Looks up a 3pid in the passed identity server. | ||
Args: | ||
id_server (str): The server name (including protocol and port, if required) | ||
of the identity server to use. | ||
medium (str): The type of the third party identifier (e.g. "email"). | ||
address (str): The third party identifier (e.g. "[email protected]"). | ||
id_access_token (str|None): The access token to authenticate to the identity | ||
server with | ||
Returns: | ||
str|None: the matrix ID of the 3pid, or None if it is not recognized. | ||
""" | ||
# If an access token is present, add it to the query params of the hash_details request | ||
query_params = {} | ||
if id_access_token is not None: | ||
query_params["id_access_token"] = id_access_token | ||
|
||
# Check what hashing details are supported by this identity server | ||
use_v1 = False | ||
hash_details = None | ||
try: | ||
hash_details = yield self.http_client.get_json( | ||
"%s/_matrix/identity/v2/hash_details" % (id_server, ), query_params | ||
) | ||
if not isinstance(hash_details, dict): | ||
logger.warn( | ||
"Got non-dict object when checking hash details of %s: %s", | ||
id_server, | ||
hash_details, | ||
) | ||
raise SynapseError( | ||
500, "Invalid hash details received from identity server" | ||
) | ||
except Exception as e: | ||
# Catch HttpResponseExcept for a non-200 response code | ||
# Check if this identity server does not know about v2 lookups | ||
if isinstance(e, HttpResponseException) and e.code == 404: | ||
# This is an old identity server that does not yet support v2 lookups | ||
use_v1 = True | ||
else: | ||
logger.warn("Error when looking up hashing details: %s" % (e,)) | ||
raise e | ||
|
||
if use_v1: | ||
return (yield self._lookup_3pid_v1(id_server, medium, address)) | ||
|
||
return ( | ||
yield self._lookup_3pid_v2( | ||
id_server, id_access_token, medium, address, hash_details | ||
) | ||
) | ||
|
||
@defer.inlineCallbacks | ||
def _lookup_3pid_v1(self, id_server, medium, address): | ||
"""Looks up a 3pid in the passed identity server using v1 lookup. | ||
Args: | ||
id_server (str): The server name (including protocol and port, if required) | ||
of the identity server to use. | ||
medium (str): The type of the third party identifier (e.g. "email"). | ||
address (str): The third party identifier (e.g. "[email protected]"). | ||
Returns: | ||
str: the matrix ID of the 3pid, or None if it is not recognized. | ||
""" | ||
try: | ||
data = yield self.http_client.get_json( | ||
"%s/_matrix/identity/api/v1/lookup" % (id_server), | ||
{"medium": medium, "address": address}, | ||
) | ||
|
||
if "mxid" in data: | ||
if "signatures" not in data: | ||
raise AuthError(401, "No signatures on 3pid binding") | ||
yield self._verify_any_signature(data, id_server) | ||
return data["mxid"] | ||
|
||
except IOError as e: | ||
logger.warn("Error from v1 identity server lookup: %s" % (e,)) | ||
|
||
return None | ||
|
||
@defer.inlineCallbacks | ||
def _lookup_3pid_v2( | ||
self, id_server, id_access_token, medium, address, hash_details | ||
): | ||
"""Looks up a 3pid in the passed identity server using v2 lookup. | ||
Args: | ||
id_server (str): The server name (including protocol and port, if required) | ||
of the identity server to use. | ||
id_access_token (str): The access token to authenticate to the identity server with | ||
medium (str): The type of the third party identifier (e.g. "email"). | ||
address (str): The third party identifier (e.g. "[email protected]"). | ||
hash_details (dict[str, str|list]): A dictionary containing hashing information | ||
provided by an identity server. | ||
Returns: | ||
Deferred[str|None]: the matrix ID of the 3pid, or None if it is not recognised. | ||
""" | ||
# Extract information from hash_details | ||
supported_lookup_algorithms = hash_details.get("algorithms") | ||
lookup_pepper = hash_details.get("lookup_pepper") | ||
if not supported_lookup_algorithms or lookup_pepper: | ||
raise SynapseError( | ||
500, "Invalid hash details received from identity server" | ||
) | ||
|
||
# Check if any of the supported lookup algorithms are present | ||
if LookupAlgorithm.SHA256 in supported_lookup_algorithms: | ||
# Perform a hashed lookup | ||
lookup_algorithm = LookupAlgorithm.SHA256 | ||
|
||
# Hash address, medium and the pepper with sha256 | ||
to_hash = "%s %s %s" % (address, medium, lookup_pepper) | ||
lookup_value = sha256_and_url_safe_base64(to_hash) | ||
|
||
elif LookupAlgorithm.NONE in supported_lookup_algorithms: | ||
# Perform a non-hashed lookup | ||
lookup_algorithm = LookupAlgorithm.NONE | ||
|
||
# Combine together plaintext address and medium | ||
lookup_value = "%s %s" % (address, medium) | ||
|
||
else: | ||
logger.warn( | ||
"None of the provided lookup algorithms of %s are supported: %s", | ||
id_server, | ||
supported_lookup_algorithms, | ||
) | ||
raise SynapseError( | ||
400, | ||
"Provided identity server does not support any v2 lookup " | ||
"algorithms that this homeserver supports.", | ||
) | ||
|
||
try: | ||
lookup_results = yield self.http_client.post_json_get_json( | ||
"%s/_matrix/identity/v2/lookup" % id_server, | ||
{ | ||
"id_access_token": id_access_token, | ||
"addresses": [lookup_value], | ||
"algorithm": lookup_algorithm, | ||
"pepper": lookup_pepper, | ||
}, | ||
) | ||
except Exception as e: | ||
logger.warn("Error when performing a v2 3pid lookup: %s" % (e,)) | ||
raise SynapseError( | ||
500, "Unknown error occurred during identity server lookup" | ||
) | ||
|
||
# Check for a mapping from what we looked up to an MXID | ||
if "mappings" not in lookup_results or not isinstance( | ||
lookup_results["mappings"], dict | ||
): | ||
logger.debug("No results from 3pid lookup") | ||
return None | ||
|
||
# Return the MXID if it's available, or None otherwise | ||
mxid = lookup_results["mappings"].get(lookup_value) | ||
return mxid | ||
|
||
@defer.inlineCallbacks | ||
def _verify_any_signature(self, data, server_hostname): | ||
if server_hostname not in data["signatures"]: | ||
raise AuthError(401, "No signature from server %s" % (server_hostname,)) | ||
for key_name, signature in data["signatures"][server_hostname].items(): | ||
key_data = yield self.http_client.get_json( | ||
"%s/_matrix/identity/api/v1/pubkey/%s" % (server_hostname, key_name) | ||
) | ||
if "public_key" not in key_data: | ||
raise AuthError( | ||
401, "No public key named %s from %s" % (key_name, server_hostname) | ||
) | ||
verify_signed_json( | ||
data, | ||
server_hostname, | ||
decode_verify_key_bytes( | ||
key_name, decode_base64(key_data["public_key"]) | ||
), | ||
) | ||
return | ||
|
||
|
||
class LookupAlgorithm: | ||
""" | ||
|
Oops, something went wrong.