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

Stop using deprecated keyIds param on /key/v2/server #14525

Merged
merged 5 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/14490.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Stop using deprecated `keyIds` parameter when calling `/_matrix/key/v2/server`.
1 change: 0 additions & 1 deletion changelog.d/14490.misc

This file was deleted.

1 change: 1 addition & 0 deletions changelog.d/14525.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Stop using deprecated `keyIds` parameter when calling `/_matrix/key/v2/server`.
richvdh marked this conversation as resolved.
Show resolved Hide resolved
107 changes: 43 additions & 64 deletions synapse/crypto/keyring.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import abc
import logging
import urllib
from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Optional, Tuple

import attr
Expand Down Expand Up @@ -813,89 +812,69 @@ async def _fetch_keys(

results = {}

async def get_key(key_to_fetch_item: _FetchKeyRequest) -> None:
async def get_keys(key_to_fetch_item: _FetchKeyRequest) -> None:
server_name = key_to_fetch_item.server_name
key_ids = key_to_fetch_item.key_ids
Copy link
Member

Choose a reason for hiding this comment

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

I did wonder if key_ids was even needed here now, but it seems to be used quite a bit.


try:
keys = await self.get_server_verify_key_v2_direct(server_name, key_ids)
keys = await self.get_server_verify_keys_v2_direct(server_name)
results[server_name] = keys
except KeyLookupError as e:
logger.warning(
"Error looking up keys %s from %s: %s", key_ids, server_name, e
)
logger.warning("Error looking up keys from %s: %s", server_name, e)
except Exception:
logger.exception("Error getting keys %s from %s", key_ids, server_name)
logger.exception("Error getting keys from %s", server_name)

await yieldable_gather_results(get_key, keys_to_fetch)
await yieldable_gather_results(get_keys, keys_to_fetch)
return results

async def get_server_verify_key_v2_direct(
self, server_name: str, key_ids: Iterable[str]
async def get_server_verify_keys_v2_direct(
self, server_name: str
) -> Dict[str, FetchKeyResult]:
"""

Args:
server_name:
key_ids:
server_name: Server to request keys from

Returns:
Map from key ID to lookup result

Raises:
KeyLookupError if there was a problem making the lookup
"""
keys: Dict[str, FetchKeyResult] = {}

for requested_key_id in key_ids:
# we may have found this key as a side-effect of asking for another.
if requested_key_id in keys:
continue

time_now_ms = self.clock.time_msec()
try:
response = await self.client.get_json(
destination=server_name,
path="/_matrix/key/v2/server/"
+ urllib.parse.quote(requested_key_id, safe=""),
ignore_backoff=True,
# we only give the remote server 10s to respond. It should be an
# easy request to handle, so if it doesn't reply within 10s, it's
# probably not going to.
#
# Furthermore, when we are acting as a notary server, we cannot
# wait all day for all of the origin servers, as the requesting
# server will otherwise time out before we can respond.
#
# (Note that get_json may make 4 attempts, so this can still take
# almost 45 seconds to fetch the headers, plus up to another 60s to
# read the response).
timeout=10000,
)
except (NotRetryingDestination, RequestSendFailed) as e:
# these both have str() representations which we can't really improve
# upon
raise KeyLookupError(str(e))
except HttpResponseException as e:
raise KeyLookupError("Remote server returned an error: %s" % (e,))

assert isinstance(response, dict)
if response["server_name"] != server_name:
raise KeyLookupError(
"Expected a response for server %r not %r"
% (server_name, response["server_name"])
)

response_keys = await self.process_v2_response(
from_server=server_name,
response_json=response,
time_added_ms=time_now_ms,
time_now_ms = self.clock.time_msec()
try:
response = await self.client.get_json(
destination=server_name,
path="/_matrix/key/v2/server",
ignore_backoff=True,
# we only give the remote server 10s to respond. It should be an
# easy request to handle, so if it doesn't reply within 10s, it's
# probably not going to.
#
# Furthermore, when we are acting as a notary server, we cannot
# wait all day for all of the origin servers, as the requesting
# server will otherwise time out before we can respond.
#
# (Note that get_json may make 4 attempts, so this can still take
# almost 45 seconds to fetch the headers, plus up to another 60s to
# read the response).
timeout=10000,
)
await self.store.store_server_verify_keys(
server_name,
time_now_ms,
((server_name, key_id, key) for key_id, key in response_keys.items()),
except (NotRetryingDestination, RequestSendFailed) as e:
# these both have str() representations which we can't really improve
# upon
raise KeyLookupError(str(e))
except HttpResponseException as e:
raise KeyLookupError("Remote server returned an error: %s" % (e,))

assert isinstance(response, dict)
if response["server_name"] != server_name:
raise KeyLookupError(
"Expected a response for server %r not %r"
% (server_name, response["server_name"])
)
keys.update(response_keys)

return keys
return await self.process_v2_response(
from_server=server_name,
response_json=response,
time_added_ms=time_now_ms,
)
14 changes: 1 addition & 13 deletions tests/crypto/test_keyring.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ def test_get_keys_from_server(self):

async def get_json(destination, path, **kwargs):
self.assertEqual(destination, SERVER_NAME)
self.assertEqual(path, "/_matrix/key/v2/server/key1")
self.assertEqual(path, "/_matrix/key/v2/server")
return response

self.http_client.get_json.side_effect = get_json
Expand Down Expand Up @@ -469,18 +469,6 @@ async def get_json(destination, path, **kwargs):
keys = self.get_success(fetcher.get_keys(SERVER_NAME, ["key1"], 0))
self.assertEqual(keys, {})

def test_keyid_containing_forward_slash(self) -> None:
"""We should url-encode any url unsafe chars in key ids.

Detects https://github.com/matrix-org/synapse/issues/14488.
"""
fetcher = ServerKeyFetcher(self.hs)
self.get_success(fetcher.get_keys("example.com", ["key/potato"], 0))

self.http_client.get_json.assert_called_once()
args, kwargs = self.http_client.get_json.call_args
self.assertEqual(kwargs["path"], "/_matrix/key/v2/server/key%2Fpotato")


class PerspectivesKeyFetcherTestCase(unittest.HomeserverTestCase):
def make_homeserver(self, reactor, clock):
Expand Down
5 changes: 1 addition & 4 deletions tests/rest/key/v2/test_remote_key_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# 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 urllib.parse
from io import BytesIO, StringIO
from typing import Any, Dict, Optional, Union
from unittest.mock import Mock
Expand Down Expand Up @@ -65,9 +64,7 @@ async def get_json(
self.assertTrue(ignore_backoff)
self.assertEqual(destination, server_name)
key_id = "%s:%s" % (signing_key.alg, signing_key.version)
self.assertEqual(
path, "/_matrix/key/v2/server/%s" % (urllib.parse.quote(key_id),)
)
self.assertEqual(path, "/_matrix/key/v2/server")

response = {
"server_name": server_name,
Expand Down