From de29c13d41e9ae0b469597ae86a755db70df1fcd Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Thu, 26 Jun 2025 15:05:48 +0100 Subject: [PATCH] Fix backwards compat for `DirectServeJsonResource` (#18600) As that appears in the module API. Broke in #18595. --- Cargo.lock | 14 +++++++--- changelog.d/18600.misc | 1 + synapse/http/additional_resource.py | 2 +- synapse/http/server.py | 26 ++++++++++++++++--- synapse/rest/consent/consent_resource.py | 2 +- .../synapse/client/federation_whitelist.py | 2 +- synapse/rest/synapse/client/jwks.py | 2 +- .../rest/synapse/client/new_user_consent.py | 2 +- .../oidc/backchannel_logout_resource.py | 2 +- .../synapse/client/oidc/callback_resource.py | 2 +- synapse/rest/synapse/client/password_reset.py | 2 +- synapse/rest/synapse/client/pick_idp.py | 2 +- synapse/rest/synapse/client/pick_username.py | 4 +-- synapse/rest/synapse/client/rendezvous.py | 2 +- .../synapse/client/saml2/response_resource.py | 2 +- synapse/rest/synapse/client/sso_register.py | 2 +- synapse/rest/synapse/client/unsubscribe.py | 2 +- synapse/rest/well_known.py | 2 +- tests/test_server.py | 12 ++++----- 19 files changed, 56 insertions(+), 29 deletions(-) create mode 100644 changelog.d/18600.misc diff --git a/Cargo.lock b/Cargo.lock index 636b4f5a6..b85f5f8b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -365,7 +371,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "headers-core", "http", @@ -485,7 +491,7 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -1053,7 +1059,7 @@ version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-core", "futures-util", @@ -1327,7 +1333,7 @@ name = "synapse" version = "0.1.0" dependencies = [ "anyhow", - "base64", + "base64 0.21.7", "blake2", "bytes", "futures", diff --git a/changelog.d/18600.misc b/changelog.d/18600.misc new file mode 100644 index 000000000..b1a39b43f --- /dev/null +++ b/changelog.d/18600.misc @@ -0,0 +1 @@ +Better handling of ratelimited requests. diff --git a/synapse/http/additional_resource.py b/synapse/http/additional_resource.py index e65af8557..59eae841d 100644 --- a/synapse/http/additional_resource.py +++ b/synapse/http/additional_resource.py @@ -53,7 +53,7 @@ class AdditionalResource(DirectServeJsonResource): hs: homeserver handler: function to be called to handle the request. """ - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._handler = handler async def _async_render(self, request: Request) -> Optional[Tuple[int, Any]]: diff --git a/synapse/http/server.py b/synapse/http/server.py index ec99aa39f..395d82fd1 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -42,6 +42,7 @@ from typing import ( Protocol, Tuple, Union, + cast, ) import attr @@ -49,8 +50,9 @@ import jinja2 from canonicaljson import encode_canonical_json from zope.interface import implementer -from twisted.internet import defer, interfaces +from twisted.internet import defer, interfaces, reactor from twisted.internet.defer import CancelledError +from twisted.internet.interfaces import IReactorTime from twisted.python import failure from twisted.web import resource @@ -401,8 +403,15 @@ class DirectServeJsonResource(_AsyncResource): """ def __init__( - self, clock: Clock, canonical_json: bool = False, extract_context: bool = False + self, + canonical_json: bool = False, + extract_context: bool = False, + # Clock is optional as this class is exposed to the module API. + clock: Optional[Clock] = None, ): + if clock is None: + clock = Clock(cast(IReactorTime, reactor)) + super().__init__(clock, extract_context) self.canonical_json = canonical_json @@ -460,7 +469,7 @@ class JsonResource(DirectServeJsonResource): extract_context: bool = False, ): self.clock = hs.get_clock() - super().__init__(self.clock, canonical_json, extract_context) + super().__init__(canonical_json, extract_context, clock=self.clock) # Map of path regex -> method -> callback. self._routes: Dict[Pattern[str], Dict[bytes, _PathEntry]] = {} self.hs = hs @@ -573,6 +582,17 @@ class DirectServeHtmlResource(_AsyncResource): # The error template to use for this resource ERROR_TEMPLATE = HTML_ERROR_TEMPLATE + def __init__( + self, + extract_context: bool = False, + # Clock is optional as this class is exposed to the module API. + clock: Optional[Clock] = None, + ): + if clock is None: + clock = Clock(cast(IReactorTime, reactor)) + + super().__init__(clock, extract_context) + def _send_response( self, request: "SynapseRequest", diff --git a/synapse/rest/consent/consent_resource.py b/synapse/rest/consent/consent_resource.py index a20eaf70e..3961f8289 100644 --- a/synapse/rest/consent/consent_resource.py +++ b/synapse/rest/consent/consent_resource.py @@ -81,7 +81,7 @@ class ConsentResource(DirectServeHtmlResource): """ def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self.hs = hs self.store = hs.get_datastores().main diff --git a/synapse/rest/synapse/client/federation_whitelist.py b/synapse/rest/synapse/client/federation_whitelist.py index bccc3c3f3..f59daf842 100644 --- a/synapse/rest/synapse/client/federation_whitelist.py +++ b/synapse/rest/synapse/client/federation_whitelist.py @@ -44,7 +44,7 @@ class FederationWhitelistResource(DirectServeJsonResource): PATH = "/_synapse/client/v1/config/federation_whitelist" def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._federation_whitelist = hs.config.federation.federation_domain_whitelist diff --git a/synapse/rest/synapse/client/jwks.py b/synapse/rest/synapse/client/jwks.py index dd70a4015..e9a7c24e3 100644 --- a/synapse/rest/synapse/client/jwks.py +++ b/synapse/rest/synapse/client/jwks.py @@ -33,7 +33,7 @@ logger = logging.getLogger(__name__) class JwksResource(DirectServeJsonResource): def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock(), extract_context=True) + super().__init__(clock=hs.get_clock(), extract_context=True) # Parameters that are allowed to be exposed in the public key. # This is done manually, because authlib's private to public key conversion diff --git a/synapse/rest/synapse/client/new_user_consent.py b/synapse/rest/synapse/client/new_user_consent.py index dfe5dadf6..c7bd8de48 100644 --- a/synapse/rest/synapse/client/new_user_consent.py +++ b/synapse/rest/synapse/client/new_user_consent.py @@ -48,7 +48,7 @@ class NewUserConsentResource(DirectServeHtmlResource): """ def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._sso_handler = hs.get_sso_handler() self._server_name = hs.hostname self._consent_version = hs.config.consent.user_consent_version diff --git a/synapse/rest/synapse/client/oidc/backchannel_logout_resource.py b/synapse/rest/synapse/client/oidc/backchannel_logout_resource.py index abc39234a..114c8c2d8 100644 --- a/synapse/rest/synapse/client/oidc/backchannel_logout_resource.py +++ b/synapse/rest/synapse/client/oidc/backchannel_logout_resource.py @@ -35,7 +35,7 @@ class OIDCBackchannelLogoutResource(DirectServeJsonResource): isLeaf = 1 def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._oidc_handler = hs.get_oidc_handler() async def _async_render_POST(self, request: SynapseRequest) -> None: diff --git a/synapse/rest/synapse/client/oidc/callback_resource.py b/synapse/rest/synapse/client/oidc/callback_resource.py index 6ce259d0a..cc0213c50 100644 --- a/synapse/rest/synapse/client/oidc/callback_resource.py +++ b/synapse/rest/synapse/client/oidc/callback_resource.py @@ -35,7 +35,7 @@ class OIDCCallbackResource(DirectServeHtmlResource): isLeaf = 1 def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._oidc_handler = hs.get_oidc_handler() async def _async_render_GET(self, request: SynapseRequest) -> None: diff --git a/synapse/rest/synapse/client/password_reset.py b/synapse/rest/synapse/client/password_reset.py index 28f924f4c..377578ef8 100644 --- a/synapse/rest/synapse/client/password_reset.py +++ b/synapse/rest/synapse/client/password_reset.py @@ -47,7 +47,7 @@ class PasswordResetSubmitTokenResource(DirectServeHtmlResource): Args: hs: server """ - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self.clock = hs.get_clock() self.store = hs.get_datastores().main diff --git a/synapse/rest/synapse/client/pick_idp.py b/synapse/rest/synapse/client/pick_idp.py index e980e1e2a..9668a09c1 100644 --- a/synapse/rest/synapse/client/pick_idp.py +++ b/synapse/rest/synapse/client/pick_idp.py @@ -44,7 +44,7 @@ class PickIdpResource(DirectServeHtmlResource): """ def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._sso_handler = hs.get_sso_handler() self._sso_login_idp_picker_template = ( hs.config.sso.sso_login_idp_picker_template diff --git a/synapse/rest/synapse/client/pick_username.py b/synapse/rest/synapse/client/pick_username.py index e11c3f430..1727bb63b 100644 --- a/synapse/rest/synapse/client/pick_username.py +++ b/synapse/rest/synapse/client/pick_username.py @@ -62,7 +62,7 @@ def pick_username_resource(hs: "HomeServer") -> Resource: class AvailabilityCheckResource(DirectServeJsonResource): def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._sso_handler = hs.get_sso_handler() async def _async_render_GET(self, request: Request) -> Tuple[int, JsonDict]: @@ -78,7 +78,7 @@ class AvailabilityCheckResource(DirectServeJsonResource): class AccountDetailsResource(DirectServeHtmlResource): def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._sso_handler = hs.get_sso_handler() def template_search_dirs() -> Generator[str, None, None]: diff --git a/synapse/rest/synapse/client/rendezvous.py b/synapse/rest/synapse/client/rendezvous.py index ceb61ef7a..5278c3557 100644 --- a/synapse/rest/synapse/client/rendezvous.py +++ b/synapse/rest/synapse/client/rendezvous.py @@ -30,7 +30,7 @@ class MSC4108RendezvousSessionResource(DirectServeJsonResource): isLeaf = True def __init__(self, hs: "HomeServer") -> None: - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._handler = hs.get_rendezvous_handler() async def _async_render_GET(self, request: SynapseRequest) -> None: diff --git a/synapse/rest/synapse/client/saml2/response_resource.py b/synapse/rest/synapse/client/saml2/response_resource.py index 74e22f49b..d2cf4f21f 100644 --- a/synapse/rest/synapse/client/saml2/response_resource.py +++ b/synapse/rest/synapse/client/saml2/response_resource.py @@ -35,7 +35,7 @@ class SAML2ResponseResource(DirectServeHtmlResource): isLeaf = 1 def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._saml_handler = hs.get_saml_handler() self._sso_handler = hs.get_sso_handler() diff --git a/synapse/rest/synapse/client/sso_register.py b/synapse/rest/synapse/client/sso_register.py index 1a5afddae..74d9bdee8 100644 --- a/synapse/rest/synapse/client/sso_register.py +++ b/synapse/rest/synapse/client/sso_register.py @@ -43,7 +43,7 @@ class SsoRegisterResource(DirectServeHtmlResource): """ def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._sso_handler = hs.get_sso_handler() async def _async_render_GET(self, request: Request) -> None: diff --git a/synapse/rest/synapse/client/unsubscribe.py b/synapse/rest/synapse/client/unsubscribe.py index b5077d574..90837611d 100644 --- a/synapse/rest/synapse/client/unsubscribe.py +++ b/synapse/rest/synapse/client/unsubscribe.py @@ -38,7 +38,7 @@ class UnsubscribeResource(DirectServeHtmlResource): SUCCESS_HTML = b"You have been unsubscribed" def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self.notifier = hs.get_notifier() self.auth = hs.get_auth() self.pusher_pool = hs.get_pusherpool() diff --git a/synapse/rest/well_known.py b/synapse/rest/well_known.py index 45d81af84..b4476e5a6 100644 --- a/synapse/rest/well_known.py +++ b/synapse/rest/well_known.py @@ -86,7 +86,7 @@ class ClientWellKnownResource(DirectServeJsonResource): isLeaf = 1 def __init__(self, hs: "HomeServer"): - super().__init__(hs.get_clock()) + super().__init__(clock=hs.get_clock()) self._well_known_builder = WellKnownBuilder(hs) async def _async_render_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: diff --git a/tests/test_server.py b/tests/test_server.py index b7e62ba0e..0aa358451 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -325,7 +325,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase): request.write(b"response") request.finish() - res = WrapHtmlRequestHandlerTests.TestResource(self.clock) + res = WrapHtmlRequestHandlerTests.TestResource(clock=self.clock) res.callback = callback channel = make_request( @@ -345,7 +345,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase): async def callback(request: SynapseRequest, **kwargs: object) -> None: raise RedirectException(b"/look/an/eagle", 301) - res = WrapHtmlRequestHandlerTests.TestResource(self.clock) + res = WrapHtmlRequestHandlerTests.TestResource(clock=self.clock) res.callback = callback channel = make_request( @@ -367,7 +367,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase): e.cookies.append(b"session=yespls") raise e - res = WrapHtmlRequestHandlerTests.TestResource(self.clock) + res = WrapHtmlRequestHandlerTests.TestResource(clock=self.clock) res.callback = callback channel = make_request( @@ -388,7 +388,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase): request.write(b"response") request.finish() - res = WrapHtmlRequestHandlerTests.TestResource(self.clock) + res = WrapHtmlRequestHandlerTests.TestResource(clock=self.clock) res.callback = callback channel = make_request( @@ -401,7 +401,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase): class CancellableDirectServeJsonResource(DirectServeJsonResource): def __init__(self, clock: Clock): - super().__init__(clock) + super().__init__(clock=clock) self.clock = clock @cancellable @@ -418,7 +418,7 @@ class CancellableDirectServeHtmlResource(DirectServeHtmlResource): ERROR_TEMPLATE = "{code} {msg}" def __init__(self, clock: Clock): - super().__init__(clock) + super().__init__(clock=clock) self.clock = clock @cancellable