Browse Source

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

Fixes #14523.
tags/v1.74.0rc1
Richard van der Hoff 1 year ago
committed by GitHub
parent
commit
ecb6fe9d9c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 82 deletions
  1. +1
    -0
      changelog.d/14490.feature
  2. +0
    -1
      changelog.d/14490.misc
  3. +1
    -0
      changelog.d/14525.feature
  4. +43
    -64
      synapse/crypto/keyring.py
  5. +1
    -13
      tests/crypto/test_keyring.py
  6. +1
    -4
      tests/rest/key/v2/test_remote_key_resource.py

+ 1
- 0
changelog.d/14490.feature View File

@@ -0,0 +1 @@
Stop using deprecated `keyIds` parameter when calling `/_matrix/key/v2/server`.

+ 0
- 1
changelog.d/14490.misc View File

@@ -1 +0,0 @@
Fix a bug introduced in Synapse 0.9 where it would fail to fetch server keys whose IDs contain a forward slash.

+ 1
- 0
changelog.d/14525.feature View File

@@ -0,0 +1 @@
Stop using deprecated `keyIds` parameter when calling `/_matrix/key/v2/server`.

+ 43
- 64
synapse/crypto/keyring.py View File

@@ -14,7 +14,6 @@

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

import attr
@@ -813,31 +812,27 @@ class ServerKeyFetcher(BaseV2KeyFetcher):

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

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
@@ -845,57 +840,41 @@ class ServerKeyFetcher(BaseV2KeyFetcher):
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,
)

+ 1
- 13
tests/crypto/test_keyring.py View File

@@ -433,7 +433,7 @@ class ServerKeyFetcherTestCase(unittest.HomeserverTestCase):

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
@@ -469,18 +469,6 @@ class ServerKeyFetcherTestCase(unittest.HomeserverTestCase):
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):


+ 1
- 4
tests/rest/key/v2/test_remote_key_resource.py View File

@@ -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
@@ -65,9 +64,7 @@ class BaseRemoteKeyResourceTestCase(unittest.HomeserverTestCase):
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,


Loading…
Cancel
Save