|
@@ -29,9 +29,11 @@ from twisted.internet import defer
|
|
|
from synapse import types
|
|
|
from synapse.api.constants import EventTypes, Membership
|
|
|
from synapse.api.errors import AuthError, Codes, HttpResponseException, SynapseError
|
|
|
+from synapse.handlers.identity import LookupAlgorithm, create_id_access_token_header
|
|
|
from synapse.types import RoomID, UserID
|
|
|
from synapse.util.async_helpers import Linearizer
|
|
|
from synapse.util.distributor import user_joined_room, user_left_room
|
|
|
+from synapse.util.hash import sha256_and_url_safe_base64
|
|
|
|
|
|
from ._base import BaseHandler
|
|
|
|
|
@@ -626,7 +628,7 @@ class RoomMemberHandler(object):
|
|
|
servers.remove(room_alias.domain)
|
|
|
servers.insert(0, room_alias.domain)
|
|
|
|
|
|
- return (RoomID.from_string(room_id), servers)
|
|
|
+ return RoomID.from_string(room_id), servers
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
def _get_inviter(self, user_id, room_id):
|
|
@@ -638,7 +640,15 @@ class RoomMemberHandler(object):
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
def do_3pid_invite(
|
|
|
- self, room_id, inviter, medium, address, id_server, requester, txn_id
|
|
|
+ self,
|
|
|
+ room_id,
|
|
|
+ inviter,
|
|
|
+ medium,
|
|
|
+ address,
|
|
|
+ id_server,
|
|
|
+ requester,
|
|
|
+ txn_id,
|
|
|
+ id_access_token=None,
|
|
|
):
|
|
|
if self.config.block_non_admin_invites:
|
|
|
is_requester_admin = yield self.auth.is_server_admin(requester.user)
|
|
@@ -661,7 +671,12 @@ class RoomMemberHandler(object):
|
|
|
Codes.FORBIDDEN,
|
|
|
)
|
|
|
|
|
|
- invitee = yield self._lookup_3pid(id_server, medium, address)
|
|
|
+ if not self._enable_lookup:
|
|
|
+ raise SynapseError(
|
|
|
+ 403, "Looking up third-party identifiers is denied from this server"
|
|
|
+ )
|
|
|
+
|
|
|
+ invitee = yield self._lookup_3pid(id_server, medium, address, id_access_token)
|
|
|
|
|
|
if invitee:
|
|
|
yield self.update_membership(
|
|
@@ -673,9 +688,47 @@ class RoomMemberHandler(object):
|
|
|
)
|
|
|
|
|
|
@defer.inlineCallbacks
|
|
|
- def _lookup_3pid(self, id_server, medium, address):
|
|
|
+ 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 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. "foo@example.com").
|
|
|
+ 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 id_access_token is not None:
|
|
|
+ try:
|
|
|
+ results = yield self._lookup_3pid_v2(
|
|
|
+ id_server, id_access_token, medium, address
|
|
|
+ )
|
|
|
+ return results
|
|
|
+
|
|
|
+ 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
|
|
|
+ logger.warning(
|
|
|
+ "Attempted v2 lookup on v1 identity server %s. Falling "
|
|
|
+ "back to v1",
|
|
|
+ id_server,
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ logger.warning("Error when looking up hashing details: %s", e)
|
|
|
+ return None
|
|
|
+
|
|
|
+ return (yield self._lookup_3pid_v1(id_server, medium, address))
|
|
|
+
|
|
|
+ @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 port, if required)
|
|
|
of the identity server to use.
|
|
@@ -685,10 +738,6 @@ class RoomMemberHandler(object):
|
|
|
Returns:
|
|
|
str: the matrix ID of the 3pid, or None if it is not recognized.
|
|
|
"""
|
|
|
- if not self._enable_lookup:
|
|
|
- raise SynapseError(
|
|
|
- 403, "Looking up third-party identifiers is denied from this server"
|
|
|
- )
|
|
|
try:
|
|
|
data = yield self.simple_http_client.get_json(
|
|
|
"%s%s/_matrix/identity/api/v1/lookup" % (id_server_scheme, id_server),
|
|
@@ -702,9 +751,116 @@ class RoomMemberHandler(object):
|
|
|
return data["mxid"]
|
|
|
|
|
|
except IOError as e:
|
|
|
- logger.warn("Error from identity server lookup: %s" % (e,))
|
|
|
+ logger.warning("Error from v1 identity server lookup: %s" % (e,))
|
|
|
+
|
|
|
+ return None
|
|
|
+
|
|
|
+ @defer.inlineCallbacks
|
|
|
+ def _lookup_3pid_v2(self, id_server, id_access_token, medium, address):
|
|
|
+ """Looks up a 3pid in the passed identity server using v2 lookup.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ id_server (str): The server name (including 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. "foo@example.com").
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Deferred[str|None]: the matrix ID of the 3pid, or None if it is not recognised.
|
|
|
+ """
|
|
|
+ # Check what hashing details are supported by this identity server
|
|
|
+ hash_details = yield self.simple_http_client.get_json(
|
|
|
+ "%s%s/_matrix/identity/v2/hash_details" % (id_server_scheme, id_server),
|
|
|
+ {"access_token": id_access_token},
|
|
|
+ )
|
|
|
+
|
|
|
+ if not isinstance(hash_details, dict):
|
|
|
+ logger.warning(
|
|
|
+ "Got non-dict object when checking hash details of %s%s: %s",
|
|
|
+ id_server_scheme,
|
|
|
+ id_server,
|
|
|
+ hash_details,
|
|
|
+ )
|
|
|
+ raise SynapseError(
|
|
|
+ 400,
|
|
|
+ "Non-dict object from %s%s during v2 hash_details request: %s"
|
|
|
+ % (id_server_scheme, id_server, hash_details),
|
|
|
+ )
|
|
|
+
|
|
|
+ # 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 not isinstance(supported_lookup_algorithms, list)
|
|
|
+ or not lookup_pepper
|
|
|
+ or not isinstance(lookup_pepper, str)
|
|
|
+ ):
|
|
|
+ raise SynapseError(
|
|
|
+ 400,
|
|
|
+ "Invalid hash details received from identity server %s%s: %s"
|
|
|
+ % (id_server_scheme, id_server, hash_details),
|
|
|
+ )
|
|
|
+
|
|
|
+ # 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.warning(
|
|
|
+ "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.",
|
|
|
+ )
|
|
|
+
|
|
|
+ # Authenticate with identity server given the access token from the client
|
|
|
+ headers = {"Authorization": create_id_access_token_header(id_access_token)}
|
|
|
+
|
|
|
+ try:
|
|
|
+ lookup_results = yield self.simple_http_client.post_json_get_json(
|
|
|
+ "%s%s/_matrix/identity/v2/lookup" % (id_server_scheme, id_server),
|
|
|
+ {
|
|
|
+ "addresses": [lookup_value],
|
|
|
+ "algorithm": lookup_algorithm,
|
|
|
+ "pepper": lookup_pepper,
|
|
|
+ },
|
|
|
+ headers=headers,
|
|
|
+ )
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning("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.warning("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"]:
|
|
@@ -844,7 +1000,6 @@ class RoomMemberHandler(object):
|
|
|
display_name (str): A user-friendly name to represent the invited
|
|
|
user.
|
|
|
"""
|
|
|
-
|
|
|
is_url = "%s%s/_matrix/identity/api/v1/store-invite" % (
|
|
|
id_server_scheme,
|
|
|
id_server,
|
|
@@ -862,7 +1017,6 @@ class RoomMemberHandler(object):
|
|
|
"sender_display_name": inviter_display_name,
|
|
|
"sender_avatar_url": inviter_avatar_url,
|
|
|
}
|
|
|
-
|
|
|
try:
|
|
|
data = yield self.simple_http_client.post_json_get_json(
|
|
|
is_url, invite_config
|
|
@@ -1049,7 +1203,7 @@ class RoomMemberMasterHandler(RoomMemberHandler):
|
|
|
# The 'except' clause is very broad, but we need to
|
|
|
# capture everything from DNS failures upwards
|
|
|
#
|
|
|
- logger.warn("Failed to reject invite: %s", e)
|
|
|
+ logger.warning("Failed to reject invite: %s", e)
|
|
|
|
|
|
yield self.store.locally_reject_invite(target.to_string(), room_id)
|
|
|
return {}
|