diff --git a/changelog.d/5897.feature b/changelog.d/5897.feature
new file mode 100644
index 0000000000..7b10774c96
--- /dev/null
+++ b/changelog.d/5897.feature
@@ -0,0 +1 @@
+Switch to the v2 lookup API for 3PID invites.
\ No newline at end of file
diff --git a/synapse/events/spamcheck.py b/synapse/events/spamcheck.py
index f0de4d961f..c194ba53f4 100644
--- a/synapse/events/spamcheck.py
+++ b/synapse/events/spamcheck.py
@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
class SpamChecker(object):
def __init__(self, hs):
self.spam_checker = None
diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py
index 339e0dd04d..4cea75723b 100644
--- a/synapse/handlers/identity.py
+++ b/synapse/handlers/identity.py
@@ -20,18 +20,13 @@
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,
- ProxiedRequestError,
SynapseError,
)
@@ -48,26 +43,9 @@ class IdentityHandler(BaseHandler):
self.federation_http_client = hs.get_http_client()
self.trusted_id_servers = set(hs.config.trusted_third_party_id_servers)
- self.trust_any_id_server_just_for_testing_do_not_use = (
- hs.config.use_insecure_ssl_client_just_for_testing_do_not_use
- )
self.rewrite_identity_server_urls = hs.config.rewrite_identity_server_urls
self._enable_lookup = hs.config.enable_3pid_lookup
- def _should_trust_id_server(self, id_server):
- if id_server not in self.trusted_id_servers:
- if self.trust_any_id_server_just_for_testing_do_not_use:
- logger.warn(
- "Trusting untrustworthy ID server %r even though it isn't"
- " in the trusted id list for testing because"
- " 'use_insecure_ssl_client_just_for_testing_do_not_use'"
- " is set in the config",
- id_server,
- )
- else:
- return False
- return True
-
@defer.inlineCallbacks
def threepid_from_creds(self, creds):
if "id_server" in creds:
@@ -84,16 +62,17 @@ class IdentityHandler(BaseHandler):
else:
raise SynapseError(400, "No client_secret in creds")
- if not self._should_trust_id_server(id_server):
+ if not should_trust_id_server(self.hs, id_server):
logger.warn(
"%s is not a trusted ID server: rejecting 3pid " + "credentials",
id_server,
)
return None
+
# if we have a rewrite rule set for the identity server,
# apply it now.
- if id_server in self.rewrite_identity_server_urls:
- id_server = self.rewrite_identity_server_urls[id_server]
+ id_server = self.rewrite_identity_server_urls.get(id_server, id_server)
+
try:
data = yield self.http_client.get_json(
"https://%s%s"
@@ -130,10 +109,7 @@ class IdentityHandler(BaseHandler):
# if we have a rewrite rule set for the identity server,
# apply it now, but only for sending the request (not
# storing in the database).
- if id_server in self.rewrite_identity_server_urls:
- id_server_host = self.rewrite_identity_server_urls[id_server]
- else:
- id_server_host = id_server
+ id_server_host = self.rewrite_identity_server_urls.get(id_server, id_server)
try:
data = yield self.http_client.post_json_get_json(
@@ -205,7 +181,6 @@ class IdentityHandler(BaseHandler):
Deferred[bool]: True on success, otherwise False if the identity
server doesn't support unbinding
"""
- url = "https://%s/_matrix/identity/api/v1/3pid/unbind" % (id_server,)
content = {
"mxid": mxid,
"threepid": {"medium": threepid["medium"], "address": threepid["address"]},
@@ -228,8 +203,7 @@ class IdentityHandler(BaseHandler):
#
# Note that destination_is has to be the real id_server, not
# the server we connect to.
- if id_server in self.rewrite_identity_server_urls:
- id_server = self.rewrite_identity_server_urls[id_server]
+ id_server = self.rewrite_identity_server_urls.get(id_server, id_server)
url = "https://%s/_matrix/identity/api/v1/3pid/unbind" % (id_server,)
@@ -258,7 +232,7 @@ class IdentityHandler(BaseHandler):
def requestEmailToken(
self, id_server, email, client_secret, send_attempt, next_link=None
):
- if not self._should_trust_id_server(id_server):
+ if not should_trust_id_server(self.hs, id_server):
raise SynapseError(
400, "Untrusted ID server '%s'" % id_server, Codes.SERVER_NOT_TRUSTED
)
@@ -269,10 +243,8 @@ class IdentityHandler(BaseHandler):
"send_attempt": send_attempt,
}
- # if we have a rewrite rule set for the identity server,
- # apply it now.
- if id_server in self.rewrite_identity_server_urls:
- id_server = self.rewrite_identity_server_urls[id_server]
+ # Rewrite id_server URL if necessary
+ id_server = self.rewrite_identity_server_urls.get(id_server, id_server)
if next_link:
params.update({"next_link": next_link})
@@ -292,11 +264,14 @@ class IdentityHandler(BaseHandler):
def requestMsisdnToken(
self, id_server, country, phone_number, client_secret, send_attempt, **kwargs
):
- if not self._should_trust_id_server(id_server):
+ if not should_trust_id_server(self.hs, id_server):
raise SynapseError(
400, "Untrusted ID server '%s'" % id_server, Codes.SERVER_NOT_TRUSTED
)
+ # Rewrite id_server URL if necessary
+ id_server = self.rewrite_identity_server_urls.get(id_server, id_server)
+
params = {
"country": country,
"phone_number": phone_number,
@@ -319,119 +294,30 @@ class IdentityHandler(BaseHandler):
logger.info("Proxied requestToken failed: %r", e)
raise e.to_synapse_error()
- @defer.inlineCallbacks
- def lookup_3pid(self, id_server, medium, address):
- """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").
-
- Returns:
- Deferred[dict]: The result of the lookup. See
- https://matrix.org/docs/spec/identity_service/r0.1.0.html#association-lookup
- for details
- """
- if not self._should_trust_id_server(id_server):
- raise SynapseError(
- 400, "Untrusted ID server '%s'" % id_server, Codes.SERVER_NOT_TRUSTED
- )
-
- if not self._enable_lookup:
- raise AuthError(
- 403, "Looking up third-party identifiers is denied from this server"
- )
-
- target = self.rewrite_identity_server_urls.get(id_server, id_server)
-
- try:
- data = yield self.http_client.get_json(
- "https://%s/_matrix/identity/api/v1/lookup" % (target,),
- {"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)
-
- except HttpResponseException as e:
- logger.info("Proxied lookup failed: %r", e)
- raise e.to_synapse_error()
- except IOError as e:
- logger.info("Failed to contact %r: %s", id_server, e)
- raise ProxiedRequestError(503, "Failed to contact identity server")
-
- defer.returnValue(data)
-
- @defer.inlineCallbacks
- def bulk_lookup_3pid(self, id_server, threepids):
- """Looks up given 3pids in the passed identity server.
-
- Args:
- id_server (str): The server name (including port, if required)
- of the identity server to use.
- threepids ([[str, str]]): The third party identifiers to lookup, as
- a list of 2-string sized lists ([medium, address]).
-
- Returns:
- Deferred[dict]: The result of the lookup. See
- https://matrix.org/docs/spec/identity_service/r0.1.0.html#association-lookup
- for details
- """
- if not self._should_trust_id_server(id_server):
- raise SynapseError(
- 400, "Untrusted ID server '%s'" % id_server, Codes.SERVER_NOT_TRUSTED
- )
-
- if not self._enable_lookup:
- raise AuthError(
- 403, "Looking up third-party identifiers is denied from this server"
- )
-
- target = self.rewrite_identity_server_urls.get(id_server, id_server)
-
- try:
- data = yield self.http_client.post_json_get_json(
- "https://%s/_matrix/identity/api/v1/bulk_lookup" % (target,),
- {"threepids": threepids},
+def should_trust_id_server(hs, id_server):
+ if id_server not in hs.config.trusted_third_party_id_servers:
+ if hs.trust_any_id_server_just_for_testing_do_not_use:
+ logger.warn(
+ "Trusting untrustworthy ID server %r even though it isn't"
+ " in the trusted id list for testing because"
+ " 'use_insecure_ssl_client_just_for_testing_do_not_use'"
+ " is set in the config",
+ id_server,
)
+ else:
+ return False
+ return True
- except HttpResponseException as e:
- logger.info("Proxied lookup failed: %r", e)
- raise e.to_synapse_error()
- except IOError as e:
- logger.info("Failed to contact %r: %s", id_server, e)
- raise ProxiedRequestError(503, "Failed to contact identity server")
-
- defer.returnValue(data)
-
- @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():
- target = self.rewrite_identity_server_urls.get(
- server_hostname, server_hostname
- )
+class LookupAlgorithm:
+ """
+ Supported hashing algorithms when performing a 3PID lookup.
- key_data = yield self.http_client.get_json(
- "https://%s/_matrix/identity/api/v1/pubkey/%s" % (target, 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
+ SHA256 - Hashing an (address, medium, pepper) combo with sha256, then url-safe base64
+ encoding
+ NONE - Not performing any hashing. Simply sending an (address, medium) combo in plaintext
+ """
- raise AuthError(401, "No signature from server %s" % (server_hostname,))
+ SHA256 = "sha256"
+ NONE = "none"
diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py
index 3c00151042..398c377216 100644
--- a/synapse/handlers/room_member.py
+++ b/synapse/handlers/room_member.py
@@ -20,11 +20,14 @@ import logging
from six.moves import http_client
+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 import types
from synapse.api.constants import EventTypes, Membership
-from synapse.api.ratelimiting import Ratelimiter
from synapse.api.errors import (
AuthError,
Codes,
@@ -32,9 +35,13 @@ from synapse.api.errors import (
HttpResponseException,
SynapseError,
)
+from synapse.handlers.identity import LookupAlgorithm, should_trust_id_server
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
logger = logging.getLogger(__name__)
@@ -78,7 +85,11 @@ class RoomMemberHandler(object):
self.rewrite_identity_server_urls = self.config.rewrite_identity_server_urls
self._enable_lookup = hs.config.enable_3pid_lookup
self.allow_per_room_profiles = self.config.allow_per_room_profiles
- self.ratelimiter = Ratelimiter()
+
+ # This is only used to get at ratelimit function, and
+ # maybe_kick_guest_users. It's fine there are multiple of these as
+ # it doesn't store state.
+ self.base_handler = BaseHandler(hs)
@abc.abstractmethod
def _remote_join(self, requester, remote_room_hosts, room_id, user, content):
@@ -572,7 +583,7 @@ class RoomMemberHandler(object):
event (SynapseEvent): The membership event.
context: The context of the event.
is_guest (bool): Whether the sender is a guest.
- room_hosts ([str]): Homeservers which are likely to already be in
+ remote_room_hosts (list[str]|None): Homeservers which are likely to already be in
the room, and could be danced with in order to join this
homeserver for the first time.
ratelimit (bool): Whether to rate limit this request.
@@ -683,7 +694,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):
@@ -714,7 +725,7 @@ class RoomMemberHandler(object):
# We need to rate limit *before* we send out any 3PID invites, so we
# can't just rely on the standard ratelimiting of events.
- self.ratelimiter.ratelimit(
+ self.base_handler.ratelimiter.ratelimit(
requester.user.to_string(),
time_now_s=self.hs.clock.time(),
rate_hz=self.hs.config.rc_third_party_invite.per_second,
@@ -742,7 +753,7 @@ class RoomMemberHandler(object):
Codes.FORBIDDEN,
)
- invitee = yield self._lookup_3pid(id_server, medium, address)
+ invitee = yield self.lookup_3pid(id_server, medium, address)
is_published = yield self.store.is_room_published(room_id)
@@ -766,23 +777,60 @@ class RoomMemberHandler(object):
requester, id_server, medium, address, room_id, inviter, txn_id=txn_id
)
- def _get_id_server_target(self, id_server):
- """Looks up an id_server's actual http endpoint
+ @defer.inlineCallbacks
+ def lookup_3pid(self, id_server, medium, address):
+ """Looks up a 3pid in the passed identity server.
Args:
- id_server (str): the server name to lookup.
+ 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").
Returns:
- the http endpoint to connect to.
+ str: the matrix ID of the 3pid, or None if it is not recognized.
"""
- if id_server in self.rewrite_identity_server_urls:
- return self.rewrite_identity_server_urls[id_server]
+ if not should_trust_id_server(self.hs, id_server):
+ raise SynapseError(
+ 400, "Untrusted ID server '%s'" % id_server,
+ Codes.SERVER_NOT_TRUSTED
+ )
+
+ if not self._enable_lookup:
+ raise SynapseError(
+ 403, "Looking up third-party identifiers is denied from this server"
+ )
- return id_server
+ # Rewrite id_server URL if necessary
+ id_server = self.rewrite_identity_server_urls.get(id_server, id_server)
+
+ # Check what hashing details are supported by this identity server
+ use_v1 = False
+ hash_details = None
+ try:
+ hash_details = yield self.simple_http_client.get_json(
+ "%s%s/_matrix/identity/v2/hash_details" % (id_server_scheme, id_server)
+ )
+ except (HttpResponseException, ValueError) as e:
+ # Catch HttpResponseExcept for a non-200 response code
+ # Catch ValueError for non-JSON response body
+
+ # Check if this identity server does not know about v2 lookups
+ if 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,))
+ return None
+
+ if use_v1:
+ return (yield self._lookup_3pid_v1(id_server, medium, address))
+
+ return (yield self._lookup_3pid_v2(id_server, medium, address, hash_details))
@defer.inlineCallbacks
- def _lookup_3pid(self, id_server, medium, address):
- """Looks up a 3pid in the passed identity server.
+ 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)
@@ -794,12 +842,164 @@ class RoomMemberHandler(object):
str: the matrix ID of the 3pid, or None if it is not recognized.
"""
try:
- data = yield self.identity_handler.lookup_3pid(id_server, medium, address)
- return data.get("mxid")
+ data = yield self.simple_http_client.get_json(
+ "%s%s/_matrix/identity/api/v1/lookup" % (id_server_scheme, 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 ProxiedRequestError as e:
logger.warn("Error from identity server lookup: %s" % (e,))
+
+ except IOError as e:
+ logger.warn("Error from identity server lookup: %s" % (e,))
+
+ return None
+
+ @defer.inlineCallbacks
+ def _lookup_3pid_v2(self, id_server, medium, address, hash_details):
+ """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.
+ medium (str): The type of the third party identifier (e.g. "email").
+ address (str): The third party identifier (e.g. "foo@example.com").
+ 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["algorithms"]
+ lookup_pepper = hash_details["lookup_pepper"]
+
+ # 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%s are supported: %s",
+ id_server_scheme,
+ id_server,
+ hash_details["algorithms"],
+ )
+ raise SynapseError(
+ 400,
+ "Provided identity server does not support any v2 lookup "
+ "algorithms that this homeserver supports.",
+ )
+
+ 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,
+ },
+ )
+ except (HttpResponseException, ValueError) as e:
+ # Catch HttpResponseExcept for a non-200 response code
+ # Catch ValueError for non-JSON response body
+ logger.warn("Error when performing a 3pid lookup: %s" % (e,))
+ return None
+
+ # 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 bulk_lookup_3pid(self, id_server, threepids):
+ """Looks up given 3pids in the passed identity server.
+ Args:
+ id_server (str): The server name (including port, if required)
+ of the identity server to use.
+ threepids ([[str, str]]): The third party identifiers to lookup, as
+ a list of 2-string sized lists ([medium, address]).
+ Returns:
+ Deferred[dict]: The result of the lookup. See
+ https://matrix.org/docs/spec/identity_service/r0.1.0.html#association-lookup
+ for details
+ """
+ if not should_trust_id_server(self.hs, id_server):
+ raise SynapseError(
+ 400, "Untrusted ID server '%s'" % id_server,
+ Codes.SERVER_NOT_TRUSTED
+ )
+
+ if not self._enable_lookup:
+ raise AuthError(
+ 403, "Looking up third-party identifiers is denied from this server",
+ )
+
+ target = self.rewrite_identity_server_urls.get(id_server, id_server)
+
+ try:
+ data = yield self.simple_http_client.post_json_get_json(
+ "https://%s/_matrix/identity/api/v1/bulk_lookup" % (target,),
+ {
+ "threepids": threepids,
+ }
+ )
+
+ except HttpResponseException as e:
+ logger.info("Proxied lookup failed: %r", e)
+ raise e.to_synapse_error()
+ except IOError as e:
+ logger.info("Failed to contact %r: %s", id_server, e)
+ raise ProxiedRequestError(503, "Failed to contact identity server")
+
+ defer.returnValue(data)
+
+ @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.simple_http_client.get_json(
+ "%s%s/_matrix/identity/api/v1/pubkey/%s"
+ % (id_server_scheme, 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
+
@defer.inlineCallbacks
def _make_and_store_3pid_invite(
self, requester, id_server, medium, address, room_id, user, txn_id
@@ -917,11 +1117,10 @@ class RoomMemberHandler(object):
display_name (str): A user-friendly name to represent the invited
user.
"""
-
- target = self._get_id_server_target(id_server)
+ id_server = self.rewrite_identity_server_urls.get(id_server, id_server)
is_url = "%s%s/_matrix/identity/api/v1/store-invite" % (
id_server_scheme,
- target,
+ id_server,
)
invite_config = {
@@ -961,7 +1160,7 @@ class RoomMemberHandler(object):
fallback_public_key = {
"public_key": data["public_key"],
"key_validity_url": "%s%s/_matrix/identity/api/v1/pubkey/isvalid"
- % (id_server_scheme, target),
+ % (id_server_scheme, id_server),
}
else:
fallback_public_key = public_keys[0]
@@ -1028,9 +1227,7 @@ class RoomMemberMasterHandler(RoomMemberHandler):
)
if complexity:
- if complexity["v1"] > max_complexity:
- return True
- return False
+ return complexity["v1"] > max_complexity
return None
@defer.inlineCallbacks
@@ -1046,10 +1243,7 @@ class RoomMemberMasterHandler(RoomMemberHandler):
max_complexity = self.hs.config.limit_remote_rooms.complexity
complexity = yield self.store.get_room_complexity(room_id)
- if complexity["v1"] > max_complexity:
- return True
-
- return False
+ return complexity["v1"] > max_complexity
@defer.inlineCallbacks
def _remote_join(self, requester, remote_room_hosts, room_id, user, content):
diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py
index b83704fed4..986f56d3d7 100644
--- a/synapse/rest/client/v2_alpha/account.py
+++ b/synapse/rest/client/v2_alpha/account.py
@@ -706,6 +706,7 @@ class ThreepidLookupRestServlet(RestServlet):
super(ThreepidLookupRestServlet, self).__init__()
self.auth = hs.get_auth()
self.identity_handler = hs.get_handlers().identity_handler
+ self.room_member_handler = hs.get_room_member_handler()
@defer.inlineCallbacks
def on_GET(self, request):
@@ -725,7 +726,7 @@ class ThreepidLookupRestServlet(RestServlet):
# Proxy the request to the identity server. lookup_3pid handles checking
# if the lookup is allowed so we don't need to do it here.
- ret = yield self.identity_handler.lookup_3pid(id_server, medium, address)
+ ret = yield self.room_member_handler.lookup_3pid(id_server, medium, address)
defer.returnValue((200, ret))
@@ -737,6 +738,7 @@ class ThreepidBulkLookupRestServlet(RestServlet):
super(ThreepidBulkLookupRestServlet, self).__init__()
self.auth = hs.get_auth()
self.identity_handler = hs.get_handlers().identity_handler
+ self.room_member_handler = hs.get_room_member_handler()
@defer.inlineCallbacks
def on_POST(self, request):
@@ -751,7 +753,7 @@ class ThreepidBulkLookupRestServlet(RestServlet):
# Proxy the request to the identity server. lookup_3pid handles checking
# if the lookup is allowed so we don't need to do it here.
- ret = yield self.identity_handler.bulk_lookup_3pid(
+ ret = yield self.room_member_handler.bulk_lookup_3pid(
body["id_server"], body["threepids"]
)
diff --git a/synapse/util/hash.py b/synapse/util/hash.py
new file mode 100644
index 0000000000..359168704e
--- /dev/null
+++ b/synapse/util/hash.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2019 The Matrix.org Foundation C.I.C.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import hashlib
+
+import unpaddedbase64
+
+
+def sha256_and_url_safe_base64(input_text):
+ """SHA256 hash an input string, encode the digest as url-safe base64, and
+ return
+
+ :param input_text: string to hash
+ :type input_text: str
+
+ :returns a sha256 hashed and url-safe base64 encoded digest
+ :rtype: str
+ """
+ digest = hashlib.sha256(input_text.encode()).digest()
+ return unpaddedbase64.encode_base64(digest, urlsafe=True)
diff --git a/tests/rest/client/test_identity.py b/tests/rest/client/test_identity.py
index f81f81602e..a7f08a3f19 100644
--- a/tests/rest/client/test_identity.py
+++ b/tests/rest/client/test_identity.py
@@ -20,6 +20,7 @@ from mock import Mock
from twisted.internet import defer
import synapse.rest.admin
+from synapse.api.errors import HttpResponseException
from synapse.rest.client.v1 import login, room
from synapse.rest.client.v2_alpha import account
@@ -109,8 +110,15 @@ class IdentityEnabledTestCase(unittest.HomeserverTestCase):
config["enable_3pid_lookup"] = True
config["trusted_third_party_id_servers"] = ["testis"]
+ def get_json(uri, args={}, headers=None):
+ # TODO: Mock v2 hash_details endpoints and don't just run the v1 code
+ if "/hash_details" in uri:
+ raise HttpResponseException(404, "I am a happy v1 server", b"{}")
+
+ return defer.succeed((200, "{}"))
+
mock_http_client = Mock(spec=["get_json", "post_json_get_json"])
- mock_http_client.get_json.return_value = defer.succeed((200, "{}"))
+ mock_http_client.get_json.side_effect = get_json
mock_http_client.post_json_get_json.return_value = defer.succeed((200, "{}"))
self.hs = self.setup_test_homeserver(
@@ -143,8 +151,10 @@ class IdentityEnabledTestCase(unittest.HomeserverTestCase):
)
self.render(request)
+ # There will be a call to hash_details, which will 404
+ # then /lookup. Thus we don't use `assert_called_with_once` here
get_json = self.hs.get_simple_http_client().get_json
- get_json.assert_called_once_with(
+ get_json.assert_called_with(
"https://testis/_matrix/identity/api/v1/lookup",
{"address": "test@example.com", "medium": "email"},
)
@@ -157,8 +167,10 @@ class IdentityEnabledTestCase(unittest.HomeserverTestCase):
request, channel = self.make_request("GET", url, access_token=self.tok)
self.render(request)
+ # There will be a call to hash_details, which will 404
+ # then /lookup. Thus we don't use `assert_called_with_once` here
get_json = self.hs.get_simple_http_client().get_json
- get_json.assert_called_once_with(
+ get_json.assert_called_with(
"https://testis/_matrix/identity/api/v1/lookup",
{"address": "foo@bar.baz", "medium": "email"},
)
diff --git a/tests/rest/client/test_room_access_rules.py b/tests/rest/client/test_room_access_rules.py
index d44f5c2c8c..69920853e6 100644
--- a/tests/rest/client/test_room_access_rules.py
+++ b/tests/rest/client/test_room_access_rules.py
@@ -22,6 +22,7 @@ from mock import Mock
from twisted.internet import defer
from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
+from synapse.api.errors import HttpResponseException
from synapse.rest import admin
from synapse.rest.client.v1 import login, room
from synapse.third_party_rules.access_rules import (
@@ -58,6 +59,10 @@ class RoomAccessTestCase(unittest.HomeserverTestCase):
return defer.succeed(pdu)
def get_json(uri, args={}, headers=None):
+ # TODO: Mock v2 hash_details endpoints and don't just run the v1 code
+ if "/hash_details" in uri:
+ raise HttpResponseException(404, "I am a happy v1 server", b"{}")
+
address_domain = args["address"].split("@")[1]
return defer.succeed({"hs": address_domain})
@@ -332,6 +337,7 @@ class RoomAccessTestCase(unittest.HomeserverTestCase):
self.hs.config.rc_third_party_invite.burst_count = 10
self.hs.config.rc_third_party_invite.per_second = 0.1
+ # Note: These calls trigger the mocked get_json method above
# We can't send a 3PID invite to a room that already has two members.
self.send_threepid_invite(
address="test@allowed_domain",
diff --git a/tests/rest/client/v2_alpha/test_register.py b/tests/rest/client/v2_alpha/test_register.py
index 9e72cae62a..9b7eb1e856 100644
--- a/tests/rest/client/v2_alpha/test_register.py
+++ b/tests/rest/client/v2_alpha/test_register.py
@@ -52,7 +52,6 @@ class RegisterRestServletTestCase(unittest.HomeserverTestCase):
return self.hs
- @unittest.DEBUG
def test_POST_appservice_registration_valid(self):
user_id = "@as_user_kermit:test"
as_token = "i_am_an_app_service"
diff --git a/tests/rulecheck/test_domainrulecheck.py b/tests/rulecheck/test_domainrulecheck.py
index 1accc70dc9..145d6cba5b 100644
--- a/tests/rulecheck/test_domainrulecheck.py
+++ b/tests/rulecheck/test_domainrulecheck.py
@@ -15,6 +15,8 @@
import json
+from mock import Mock
+from twisted.internet import defer
import synapse.rest.admin
from synapse.config._base import ConfigError
@@ -287,6 +289,9 @@ class DomainRuleCheckerRoomTestCase(unittest.HomeserverTestCase):
def test_cannot_3pid_invite(self):
"""Test that unbound 3pid invites get rejected.
"""
+ self.hs.get_room_member_handler().lookup_3pid = Mock()
+ self.hs.get_room_member_handler().lookup_3pid.return_value = defer.succeed(None)
+
channel = self._create_room(self.admin_access_token)
assert channel.result["code"] == b"200", channel.result
|