Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Use the v2 Identity Service API for lookups (MSC2134 + MSC2140) #5976

Merged
merged 40 commits into from
Sep 11, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1954438
Use the v2 lookup API
anoadragon453 Aug 21, 2019
24ee3ae
lint
anoadragon453 Aug 21, 2019
902ef39
add changelog
anoadragon453 Aug 21, 2019
3a114fe
linter fight
anoadragon453 Aug 21, 2019
5426e13
Merge branch 'develop' into anoa/v2_lookup
anoadragon453 Aug 21, 2019
73fb6f3
Continue to support v1 lookup
anoadragon453 Aug 21, 2019
2472e2e
lint
anoadragon453 Aug 21, 2019
7bfccad
Address review comments
anoadragon453 Aug 27, 2019
75ef0f8
lint
anoadragon453 Aug 27, 2019
e68d648
small fixes and remove unnecessary Enum
anoadragon453 Aug 28, 2019
38dac27
Warn user when the id_server they chose does not support any of the h…
anoadragon453 Aug 28, 2019
8f1346d
Apply suggestions from code review
anoadragon453 Aug 28, 2019
4dc0849
lint
anoadragon453 Aug 28, 2019
849d8dc
Merge branch 'anoa/v2_lookup' of github.com:matrix-org/synapse into a…
anoadragon453 Aug 28, 2019
d9d156b
Merge branch 'develop' into anoa/v2_lookup
anoadragon453 Sep 3, 2019
42b11bd
use v2 identity service api endpoints for 3pid invites and lookup
anoadragon453 Sep 3, 2019
83021d9
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/v2_…
anoadragon453 Sep 3, 2019
07154ea
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/v2_…
anoadragon453 Sep 3, 2019
f4b7f7f
id_access_token support
anoadragon453 Sep 3, 2019
29c3489
Apply suggestions from code review
anoadragon453 Sep 4, 2019
ff5f6a0
Address review comments
anoadragon453 Sep 4, 2019
a5153af
Merge branch 'anoa/v2_lookup' of github.com:matrix-org/synapse into a…
anoadragon453 Sep 4, 2019
7f647bc
Revert moving lookup stuff to IdentityHandler
anoadragon453 Sep 4, 2019
f8bb859
Fix issues with moving stuff back to RoomMemberHandler
anoadragon453 Sep 4, 2019
1c59243
Factor our v2 invite things
anoadragon453 Sep 4, 2019
1b20928
lint
anoadragon453 Sep 4, 2019
db1d161
whoops
anoadragon453 Sep 4, 2019
9f92c3e
Change lookup_3pid back to a private method
anoadragon453 Sep 4, 2019
07169b1
Apply suggestions from code review
anoadragon453 Sep 5, 2019
5b852c2
Address review comments
anoadragon453 Sep 5, 2019
0d968c0
liiiiiiiiiiiint
anoadragon453 Sep 5, 2019
f18f3f1
address review comments
anoadragon453 Sep 9, 2019
18671b0
lint
anoadragon453 Sep 9, 2019
649dcbe
id_access_token -> access_token in query params
anoadragon453 Sep 10, 2019
b4520ea
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/v2_…
anoadragon453 Sep 11, 2019
79f5c4f
Address review comments.
anoadragon453 Sep 11, 2019
cf8dbea
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/v2_…
anoadragon453 Sep 11, 2019
7008c79
Send id access_token via Authorization headers, not JSON body
anoadragon453 Sep 11, 2019
ffb284e
Merge branch 'develop' of github.com:matrix-org/synapse into anoa/v2_…
anoadragon453 Sep 11, 2019
317dff6
Update changelog.d/5897.feature
anoadragon453 Sep 11, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5897.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Switch to the v2 lookup API for 3PID invites.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
194 changes: 194 additions & 0 deletions synapse/handlers/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -282,3 +287,192 @@ def requestMsisdnToken(
except HttpResponseException as e:
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. "foo@example.com").
id_access_token (str|None): The access token to authenticate to the identity
server with

Returns:
str: the matrix ID of the 3pid, or None if it is not recognized.
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
"""
# 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
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
)
except (HttpResponseException, ValueError) as e:
# Catch HttpResponseExcept for a non-200 response code
# Catch ValueError for non-JSON response body
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're doing this, should we check for other exceptions like "failed to connect"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think ValueError doesn't have a code attribute either, which would cause this code to fail.

Should we drop ValueError?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mm, yeah I'm leaning towards dropping ValueError since that's considered a different error from an old IS.


# 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,))
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
return None

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. "foo@example.com").

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 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we allow this to be None, or not? currently we can pass in None sometimes...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically _lookup_3pid_v2 should never be called if id_access_token is None, as hash_details is an authenticated endpoint.

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
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
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"]
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
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 are supported: %s",
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.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 (HttpResponseException, ValueError) as e:
# Catch HttpResponseExcept for a non-200 response code
# Catch ValueError for non-JSON response body
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
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 _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:
"""
Supported hashing algorithms when performing a 3PID lookup.

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
"""

SHA256 = "sha256"
NONE = "none"
4 changes: 3 additions & 1 deletion synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,8 +579,8 @@ def create_room(self, requester, config, ratelimit=True, creator_join_profile=No

room_id = yield self._generate_room_id(creator_id=user_id, is_public=is_public)

directory_handler = self.hs.get_handlers().directory_handler
if room_alias:
directory_handler = self.hs.get_handlers().directory_handler
yield directory_handler.create_association(
requester=requester,
room_id=room_id,
Expand Down Expand Up @@ -665,6 +665,7 @@ def create_room(self, requester, config, ratelimit=True, creator_join_profile=No

for invite_3pid in invite_3pid_list:
id_server = invite_3pid["id_server"]
id_access_token = invite_3pid.get("id_access_token") # optional
address = invite_3pid["address"]
medium = invite_3pid["medium"]
yield self.hs.get_room_member_handler().do_3pid_invite(
Expand All @@ -675,6 +676,7 @@ def create_room(self, requester, config, ratelimit=True, creator_join_profile=No
id_server,
requester,
txn_id=None,
id_access_token=id_access_token,
)

result = {"room_id": room_id}
Expand Down
Loading