From 23ed267aefef87ec2937b5d01c8f3121cbb843b5 Mon Sep 17 00:00:00 2001 From: MTRNord <mtrnord1@gmail.com> Date: Sat, 2 Sep 2023 19:58:31 +0200 Subject: [PATCH 1/6] Send the opentracing span information also to appservices --- synapse/appservice/api.py | 54 ++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index de7a94bf2643..39e184f343b8 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -40,6 +40,7 @@ from synapse.events import EventBase from synapse.events.utils import SerializeEventConfig, serialize_event from synapse.http.client import SimpleHttpClient, is_unknown_endpoint +from synapse.logging import opentracing from synapse.types import DeviceListUpdates, JsonDict, ThirdPartyInstanceID from synapse.util.caches.response_cache import ResponseCache @@ -136,10 +137,15 @@ async def query_user(self, service: "ApplicationService", user_id: str) -> bool: args = None if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} + + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) response = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/users/{urllib.parse.quote(user_id)}", args, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) if response is not None: # just an empty json object return True @@ -162,10 +168,15 @@ async def query_alias(self, service: "ApplicationService", alias: str) -> bool: args = None if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} + + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) response = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/rooms/{urllib.parse.quote(alias)}", args, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) if response is not None: # just an empty json object return True @@ -203,10 +214,15 @@ async def query_3pe( **fields, b"access_token": service.hs_token, } + + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) response = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/thirdparty/{kind}/{urllib.parse.quote(protocol)}", args=args, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) if not isinstance(response, list): logger.warning( @@ -243,10 +259,15 @@ async def _get() -> Optional[JsonDict]: args = None if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} + + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) info = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/thirdparty/protocol/{urllib.parse.quote(protocol)}", args, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) if not _is_valid_3pe_metadata(info): @@ -280,10 +301,14 @@ async def ping(self, service: "ApplicationService", txn_id: Optional[str]) -> No # This is required by the configuration. assert service.hs_token is not None + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) await self.post_json_get_json( uri=f"{service.url}{APP_SERVICE_PREFIX}/ping", post_json={"transaction_id": txn_id}, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) async def push_bulk( @@ -360,11 +385,15 @@ async def push_bulk( if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) await self.put_json( f"{service.url}{APP_SERVICE_PREFIX}/transactions/{urllib.parse.quote(str(txn_id))}", json_body=body, args=args, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) if logger.isEnabledFor(logging.DEBUG): logger.debug( @@ -432,12 +461,16 @@ async def claim_client_keys( [algorithm] * count ) + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) uri = f"{service.url}/_matrix/app/unstable/org.matrix.msc3983/keys/claim" try: response = await self.post_json_get_json( uri, body, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) except HttpResponseException as e: # The appservice doesn't support this endpoint. @@ -492,13 +525,16 @@ async def query_keys( # This is required by the configuration. assert service.hs_token is not None - + headers: Dict[bytes, List[bytes]] = { + b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] + } + opentracing.inject_header_dict(headers, check_destination=False) uri = f"{service.url}/_matrix/app/unstable/org.matrix.msc3984/keys/query" try: response = await self.post_json_get_json( uri, query, - headers={"Authorization": [f"Bearer {service.hs_token}"]}, + headers=headers, ) except HttpResponseException as e: # The appservice doesn't support this endpoint. From ae4f500a87258445169e1b3169f65f0570c97a5b Mon Sep 17 00:00:00 2001 From: MTRNord <mtrnord1@gmail.com> Date: Sat, 2 Sep 2023 20:03:47 +0200 Subject: [PATCH 2/6] Add changelog entry for PR 16227 --- changelog.d/16227.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/16227.misc diff --git a/changelog.d/16227.misc b/changelog.d/16227.misc new file mode 100644 index 000000000000..510062b622c0 --- /dev/null +++ b/changelog.d/16227.misc @@ -0,0 +1 @@ +Add span information to requests sent to appservices. Contributed by MTRNord. \ No newline at end of file From 7ed9477cd7015ba14334fec9a8ea9ed207caa50b Mon Sep 17 00:00:00 2001 From: MTRNord <mtrnord1@gmail.com> Date: Sat, 2 Sep 2023 23:17:18 +0200 Subject: [PATCH 3/6] Make the changelog entry for PR 16227 a feature instead of a misc entry --- changelog.d/{16227.misc => 16227.feature} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{16227.misc => 16227.feature} (100%) diff --git a/changelog.d/16227.misc b/changelog.d/16227.feature similarity index 100% rename from changelog.d/16227.misc rename to changelog.d/16227.feature From bc990774fb4c02b2afdd8c56db99a6c24b44ac67 Mon Sep 17 00:00:00 2001 From: MTRNord <mtrnord1@gmail.com> Date: Mon, 4 Sep 2023 16:53:57 +0200 Subject: [PATCH 4/6] Update the test mock to work with bytestrings as headers --- tests/appservice/test_api.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/appservice/test_api.py b/tests/appservice/test_api.py index 75fb5fae6b92..166d2369e93e 100644 --- a/tests/appservice/test_api.py +++ b/tests/appservice/test_api.py @@ -76,7 +76,7 @@ async def get_json( headers: Mapping[Union[str, bytes], Sequence[Union[str, bytes]]], ) -> List[JsonDict]: # Ensure the access token is passed as a header. - if not headers or not headers.get("Authorization"): + if not headers or not headers.get(b"Authorization"): raise RuntimeError("Access token not provided") # ... and not as a query param if b"access_token" in args: @@ -84,7 +84,7 @@ async def get_json( "Access token should not be passed as a query param." ) - self.assertEqual(headers.get("Authorization"), [f"Bearer {TOKEN}"]) + self.assertEqual(headers.get(b"Authorization"), [f"Bearer {TOKEN}".encode()]) self.request_url = url if url == URL_USER: return SUCCESS_RESULT_USER @@ -152,11 +152,12 @@ async def get_json( # Ensure the access token is passed as a both a query param and in the headers. if not args.get(b"access_token"): raise RuntimeError("Access token should be provided in query params.") - if not headers or not headers.get("Authorization"): + if not headers or not headers.get(b"Authorization"): raise RuntimeError("Access token should be provided in auth headers.") self.assertEqual(args.get(b"access_token"), TOKEN) - self.assertEqual(headers.get("Authorization"), [f"Bearer {TOKEN}"]) + self.assertEqual(headers.get(b"Authorization"), + [f"Bearer {TOKEN}".encode()]) self.request_url = url if url == URL_USER: return SUCCESS_RESULT_USER @@ -208,10 +209,11 @@ async def post_json_get_json( headers: Mapping[Union[str, bytes], Sequence[Union[str, bytes]]], ) -> JsonDict: # Ensure the access token is passed as both a header and query arg. - if not headers.get("Authorization"): + if not headers.get(b"Authorization"): raise RuntimeError("Access token not provided") - self.assertEqual(headers.get("Authorization"), [f"Bearer {TOKEN}"]) + self.assertEqual(headers.get(b"Authorization"), + [f"Bearer {TOKEN}".encode()]) return RESPONSE # We assign to a method, which mypy doesn't like. From 44feccd0c287c369f755fe704bfb48aa5327114c Mon Sep 17 00:00:00 2001 From: MTRNord <mtrnord1@gmail.com> Date: Mon, 4 Sep 2023 20:09:54 +0200 Subject: [PATCH 5/6] Ensure file formatting of tests/appservice/test_api.py --- tests/appservice/test_api.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/appservice/test_api.py b/tests/appservice/test_api.py index 166d2369e93e..366b6fd5f00d 100644 --- a/tests/appservice/test_api.py +++ b/tests/appservice/test_api.py @@ -84,7 +84,9 @@ async def get_json( "Access token should not be passed as a query param." ) - self.assertEqual(headers.get(b"Authorization"), [f"Bearer {TOKEN}".encode()]) + self.assertEqual( + headers.get(b"Authorization"), [f"Bearer {TOKEN}".encode()] + ) self.request_url = url if url == URL_USER: return SUCCESS_RESULT_USER @@ -156,8 +158,9 @@ async def get_json( raise RuntimeError("Access token should be provided in auth headers.") self.assertEqual(args.get(b"access_token"), TOKEN) - self.assertEqual(headers.get(b"Authorization"), - [f"Bearer {TOKEN}".encode()]) + self.assertEqual( + headers.get(b"Authorization"), [f"Bearer {TOKEN}".encode()] + ) self.request_url = url if url == URL_USER: return SUCCESS_RESULT_USER @@ -212,8 +215,9 @@ async def post_json_get_json( if not headers.get(b"Authorization"): raise RuntimeError("Access token not provided") - self.assertEqual(headers.get(b"Authorization"), - [f"Bearer {TOKEN}".encode()]) + self.assertEqual( + headers.get(b"Authorization"), [f"Bearer {TOKEN}".encode()] + ) return RESPONSE # We assign to a method, which mypy doesn't like. From 2ac68cc2924944bc885fc2d208e94a075a556094 Mon Sep 17 00:00:00 2001 From: MTRNord <mtrnord1@gmail.com> Date: Wed, 6 Sep 2023 16:06:40 +0200 Subject: [PATCH 6/6] Move header generation to helper method --- synapse/appservice/api.py | 60 +++++++++++++-------------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 39e184f343b8..b1523be208e9 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -126,6 +126,17 @@ def __init__(self, hs: "HomeServer"): hs.get_clock(), "as_protocol_meta", timeout_ms=HOUR_IN_MS ) + def _get_headers(self, service: "ApplicationService") -> Dict[bytes, List[bytes]]: + """This makes sure we have always the auth header and opentracing headers set.""" + + # This is also ensured before in the functions. However this is needed to please + # the typechecks. + assert service.hs_token is not None + + headers = {b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")]} + opentracing.inject_header_dict(headers, check_destination=False) + return headers + async def query_user(self, service: "ApplicationService", user_id: str) -> bool: if service.url is None: return False @@ -138,14 +149,10 @@ async def query_user(self, service: "ApplicationService", user_id: str) -> bool: if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) response = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/users/{urllib.parse.quote(user_id)}", args, - headers=headers, + headers=self._get_headers(service), ) if response is not None: # just an empty json object return True @@ -169,14 +176,10 @@ async def query_alias(self, service: "ApplicationService", alias: str) -> bool: if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) response = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/rooms/{urllib.parse.quote(alias)}", args, - headers=headers, + headers=self._get_headers(service), ) if response is not None: # just an empty json object return True @@ -215,14 +218,10 @@ async def query_3pe( b"access_token": service.hs_token, } - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) response = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/thirdparty/{kind}/{urllib.parse.quote(protocol)}", args=args, - headers=headers, + headers=self._get_headers(service), ) if not isinstance(response, list): logger.warning( @@ -260,14 +259,10 @@ async def _get() -> Optional[JsonDict]: if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) info = await self.get_json( f"{service.url}{APP_SERVICE_PREFIX}/thirdparty/protocol/{urllib.parse.quote(protocol)}", args, - headers=headers, + headers=self._get_headers(service), ) if not _is_valid_3pe_metadata(info): @@ -301,14 +296,10 @@ async def ping(self, service: "ApplicationService", txn_id: Optional[str]) -> No # This is required by the configuration. assert service.hs_token is not None - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) await self.post_json_get_json( uri=f"{service.url}{APP_SERVICE_PREFIX}/ping", post_json={"transaction_id": txn_id}, - headers=headers, + headers=self._get_headers(service), ) async def push_bulk( @@ -385,15 +376,11 @@ async def push_bulk( if self.config.use_appservice_legacy_authorization: args = {"access_token": service.hs_token} - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) await self.put_json( f"{service.url}{APP_SERVICE_PREFIX}/transactions/{urllib.parse.quote(str(txn_id))}", json_body=body, args=args, - headers=headers, + headers=self._get_headers(service), ) if logger.isEnabledFor(logging.DEBUG): logger.debug( @@ -461,16 +448,12 @@ async def claim_client_keys( [algorithm] * count ) - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) uri = f"{service.url}/_matrix/app/unstable/org.matrix.msc3983/keys/claim" try: response = await self.post_json_get_json( uri, body, - headers=headers, + headers=self._get_headers(service), ) except HttpResponseException as e: # The appservice doesn't support this endpoint. @@ -525,16 +508,13 @@ async def query_keys( # This is required by the configuration. assert service.hs_token is not None - headers: Dict[bytes, List[bytes]] = { - b"Authorization": [b"Bearer " + service.hs_token.encode("ascii")] - } - opentracing.inject_header_dict(headers, check_destination=False) + uri = f"{service.url}/_matrix/app/unstable/org.matrix.msc3984/keys/query" try: response = await self.post_json_get_json( uri, query, - headers=headers, + headers=self._get_headers(service), ) except HttpResponseException as e: # The appservice doesn't support this endpoint.