diff --git a/rust/src/http_client.rs b/rust/src/http_client.rs index e67dae169f..ca4bf1590b 100644 --- a/rust/src/http_client.rs +++ b/rust/src/http_client.rs @@ -137,7 +137,7 @@ fn get_runtime<'a>(reactor: &Bound<'a, PyAny>) -> PyResult = OnceCell::new(); /// Access to the `twisted.internet.defer` module. -fn defer(py: Python<'_>) -> PyResult<&Bound> { +fn defer(py: Python<'_>) -> PyResult<&Bound<'_, PyAny>> { Ok(DEFER .get_or_try_init(|| py.import("twisted.internet.defer").map(Into::into))? .bind(py)) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0de6898a55..46e22a5cd6 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -11,7 +11,7 @@ pub mod http; pub mod http_client; pub mod identifier; pub mod matrix_const; -pub mod msc4108v2025_rendezvous; +pub mod msc4388_rendezvous; pub mod push; pub mod rendezvous; pub mod segmenter; @@ -55,7 +55,7 @@ fn synapse_rust(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { events::register_module(py, m)?; http_client::register_module(py, m)?; rendezvous::register_module(py, m)?; - msc4108v2025_rendezvous::register_module(py, m)?; + msc4388_rendezvous::register_module(py, m)?; segmenter::register_module(py, m)?; Ok(()) diff --git a/rust/src/msc4108v2025_rendezvous/mod.rs b/rust/src/msc4388_rendezvous/mod.rs similarity index 96% rename from rust/src/msc4108v2025_rendezvous/mod.rs rename to rust/src/msc4388_rendezvous/mod.rs index 0535711c63..02204f51d2 100644 --- a/rust/src/msc4108v2025_rendezvous/mod.rs +++ b/rust/src/msc4388_rendezvous/mod.rs @@ -58,7 +58,7 @@ fn prepare_headers(headers: &mut HeaderMap) { } #[pyclass] -struct MSC4108v2025RendezvousHandler { +struct MSC4388RendezvousHandler { clock: PyObject, sessions: BTreeMap, capacity: usize, @@ -66,7 +66,7 @@ struct MSC4108v2025RendezvousHandler { ttl: Duration, } -impl MSC4108v2025RendezvousHandler { +impl MSC4388RendezvousHandler { /// Check the length of the data parameter and throw error if invalid. fn check_data_length(&self, data: &str) -> PyResult<()> { let data_length = data.len() as u64; @@ -98,7 +98,7 @@ impl MSC4108v2025RendezvousHandler { } #[pymethods] -impl MSC4108v2025RendezvousHandler { +impl MSC4388RendezvousHandler { #[new] #[pyo3(signature = (homeserver, /, capacity=100, max_content_length=4*1024, eviction_interval=60*1000, ttl=2*60*1000))] fn new( @@ -324,7 +324,7 @@ impl MSC4108v2025RendezvousHandler { return Err(SynapseError::new( StatusCode::CONFLICT, "sequence_token does not match".to_owned(), - "IO_ELEMENT_MSC4108_CONCURRENT_WRITE", + "IO_ELEMENT_MSC4388_CONCURRENT_WRITE", None, Some(headers), )); @@ -365,9 +365,9 @@ impl MSC4108v2025RendezvousHandler { } pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { - let child_module = PyModule::new(py, "msc4108v2025_rendezvous")?; + let child_module = PyModule::new(py, "msc4388_rendezvous")?; - child_module.add_class::()?; + child_module.add_class::()?; m.add_submodule(&child_module)?; @@ -375,7 +375,7 @@ pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> // synapse.synapse_rust import rendezvous` work. py.import("sys")? .getattr("modules")? - .set_item("synapse.synapse_rust.msc4108v2025_rendezvous", child_module)?; + .set_item("synapse.synapse_rust.msc4388_rendezvous", child_module)?; Ok(()) } diff --git a/rust/src/msc4108v2025_rendezvous/session.rs b/rust/src/msc4388_rendezvous/session.rs similarity index 100% rename from rust/src/msc4108v2025_rendezvous/session.rs rename to rust/src/msc4388_rendezvous/session.rs diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index 2fff862e44..b266bb174f 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -548,25 +548,23 @@ class ExperimentalConfig(Config): ("experimental", "msc4108_delegation_endpoint"), ) - # MSC4108: Mechanism to allow OAuth 2.0 API sign in and E2EE set up via QR code - 2025 version: - msc4108v2025_mode = experimental.get("msc4108v2025_mode", "off") + # MSC4388: Secure out-of-band channel for sign in with QR: + msc4388_mode = experimental.get("msc4388_mode", "off") - if ["off", "public", "authenticated"].count(msc4108v2025_mode) != 1: + if ["off", "public", "authenticated"].count(msc4388_mode) != 1: raise ConfigError( - "msc4108v2025_mode must be one of 'off', 'public' or 'authenticated'", - ("experimental", "msc4108v2025_mode"), + "msc4388_mode must be one of 'off', 'public' or 'authenticated'", + ("experimental", "msc4388_mode"), ) - self.msc4108v2025_enabled: bool = msc4108v2025_mode != "off" - self.msc4108v2025_requires_authentication: bool = ( - msc4108v2025_mode == "authenticated" - ) + self.msc4388_enabled: bool = msc4388_mode != "off" + self.msc4388_requires_authentication: bool = msc4388_mode == "authenticated" - if self.msc4108v2025_enabled and not ( + if self.msc4388_enabled and not ( config.get("matrix_authentication_service") or {} ).get("enabled", False): raise ConfigError( - "MSC4108 2025 version requires matrix_authentication_service to be enabled", - ("experimental", "msc4108v2025_enabled"), + "MSC4388 requires matrix_authentication_service to be enabled", + ("experimental", "msc4388_enabled"), ) # MSC4133: Custom profile fields diff --git a/synapse/rest/client/rendezvous.py b/synapse/rest/client/rendezvous.py index 8baab56a4b..388942711d 100644 --- a/synapse/rest/client/rendezvous.py +++ b/synapse/rest/client/rendezvous.py @@ -68,17 +68,17 @@ class MSC4108RendezvousServlet(RestServlet): self._handler.handle_post(request) -class MSC4108v2025CreateRendezvousServlet(RestServlet): +class MSC4388CreateRendezvousServlet(RestServlet): PATTERNS = client_patterns( - "/io.element.msc4108/rendezvous$", releases=[], v1=False, unstable=True + "/io.element.msc4388/rendezvous$", releases=[], v1=False, unstable=True ) def __init__(self, hs: "HomeServer") -> None: super().__init__() - self._handler = hs.get_msc4108v2025_rendezvous_handler() + self._handler = hs.get_msc4388_rendezvous_handler() self.auth = hs.get_auth() self.require_authentication = ( - hs.config.experimental.msc4108v2025_requires_authentication + hs.config.experimental.msc4388_requires_authentication ) async def on_POST(self, request: SynapseRequest) -> None: @@ -88,9 +88,9 @@ class MSC4108v2025CreateRendezvousServlet(RestServlet): self._handler.handle_post(request) -class MSC4108v2025UpdateRendezvousServlet(RestServlet): +class MSC4388UpdateRendezvousServlet(RestServlet): PATTERNS = client_patterns( - "/io.element.msc4108/rendezvous/(?P[^/]+)$", + "/io.element.msc4388/rendezvous/(?P[^/]+)$", releases=[], v1=False, unstable=True, @@ -98,7 +98,7 @@ class MSC4108v2025UpdateRendezvousServlet(RestServlet): def __init__(self, hs: "HomeServer") -> None: super().__init__() - self._handler = hs.get_msc4108v2025_rendezvous_handler() + self._handler = hs.get_msc4388_rendezvous_handler() def on_GET(self, request: SynapseRequest, rendezvous_id: str) -> None: self._handler.handle_get(request, rendezvous_id) @@ -117,6 +117,6 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: if hs.config.experimental.msc4108_delegation_endpoint is not None: MSC4108DelegationRendezvousServlet(hs).register(http_server) - if hs.config.experimental.msc4108v2025_enabled: - MSC4108v2025CreateRendezvousServlet(hs).register(http_server) - MSC4108v2025UpdateRendezvousServlet(hs).register(http_server) + if hs.config.experimental.msc4388_enabled: + MSC4388CreateRendezvousServlet(hs).register(http_server) + MSC4388UpdateRendezvousServlet(hs).register(http_server) diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py index 590582bee7..db00a3ecb3 100644 --- a/synapse/rest/client/versions.py +++ b/synapse/rest/client/versions.py @@ -169,10 +169,8 @@ class VersionsRestServlet(RestServlet): is not None ) ), - # MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code - 2025 version - "io.element.msc4108": ( - self.config.experimental.msc4108v2025_enabled - ), + # MSC4388: Secure out-of-band channel for sign in with QR + "io.element.msc4388": (self.config.experimental.msc4388_enabled), # MSC4140: Delayed events "org.matrix.msc4140": bool(self.config.server.max_event_delay_ms), # Simplified sliding sync diff --git a/synapse/server.py b/synapse/server.py index 990e4d704a..c174c767dd 100644 --- a/synapse/server.py +++ b/synapse/server.py @@ -170,7 +170,7 @@ from synapse.state import StateHandler, StateResolutionHandler from synapse.storage import Databases from synapse.storage.controllers import StorageControllers from synapse.streams.events import EventSources -from synapse.synapse_rust.msc4108v2025_rendezvous import MSC4108v2025RendezvousHandler +from synapse.synapse_rust.msc4388_rendezvous import MSC4388RendezvousHandler from synapse.synapse_rust.rendezvous import RendezvousHandler from synapse.types import DomainSpecificString, ISynapseReactor from synapse.util import SYNAPSE_VERSION @@ -1158,8 +1158,8 @@ class HomeServer(metaclass=abc.ABCMeta): return RendezvousHandler(self) @cache_in_self - def get_msc4108v2025_rendezvous_handler(self) -> MSC4108v2025RendezvousHandler: - return MSC4108v2025RendezvousHandler(self) + def get_msc4388_rendezvous_handler(self) -> MSC4388RendezvousHandler: + return MSC4388RendezvousHandler(self) @cache_in_self def get_outbound_redis_connection(self) -> "ConnectionHandler": diff --git a/synapse/synapse_rust/msc4108v2025_rendezvous.pyi b/synapse/synapse_rust/msc4388_rendezvous.pyi similarity index 86% rename from synapse/synapse_rust/msc4108v2025_rendezvous.pyi rename to synapse/synapse_rust/msc4388_rendezvous.pyi index 514df7818a..a22cf3a017 100644 --- a/synapse/synapse_rust/msc4108v2025_rendezvous.pyi +++ b/synapse/synapse_rust/msc4388_rendezvous.pyi @@ -14,15 +14,15 @@ from twisted.web.iweb import IRequest from synapse.server import HomeServer -class MSC4108v2025RendezvousHandler: +class MSC4388RendezvousHandler: def __init__( self, homeserver: HomeServer, /, capacity: int = 100, # This should be configurable - max_content_length: int = 4 * 1024, # MSC4108 specifies maximum of 4KB + max_content_length: int = 4 * 1024, # MSC4388 specifies maximum of 4KB eviction_interval: int = 60 * 1000, - ttl: int = 2 * 60 * 1000, # MSC4108 specifies minimum of 120 seconds + ttl: int = 2 * 60 * 1000, # MSC4388 specifies minimum of 120 seconds ) -> None: ... def handle_post(self, request: IRequest) -> None: ... def handle_get(self, request: IRequest, session_id: str) -> None: ... diff --git a/tests/rest/client/test_msc4108v2025_rendezvous.py b/tests/rest/client/test_msc4388_rendezvous.py similarity index 92% rename from tests/rest/client/test_msc4108v2025_rendezvous.py rename to tests/rest/client/test_msc4388_rendezvous.py index 4ea7ab613b..5f996018f7 100644 --- a/tests/rest/client/test_msc4108v2025_rendezvous.py +++ b/tests/rest/client/test_msc4388_rendezvous.py @@ -29,12 +29,12 @@ from tests import unittest from tests.unittest import checked_cast, override_config from tests.utils import HAS_AUTHLIB -msc4108_endpoint = "/_matrix/client/unstable/io.element.msc4108/rendezvous" +rz_endpoint = "/_matrix/client/unstable/io.element.msc4388/rendezvous" class RendezvousServletTestCase(unittest.HomeserverTestCase): """ - Test the experimental MSC4108 rendezvous endpoint with the latest behaviour. + Test the experimental MSC4388 rendezvous endpoint. """ servlets = [ @@ -121,18 +121,18 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): return "mock_token_" + username + "_" + device_id def test_disabled(self) -> None: - channel = self.make_request("POST", msc4108_endpoint, {}, access_token=None) + channel = self.make_request("POST", rz_endpoint, {}, access_token=None) self.assertEqual(channel.code, 404) @override_config( { "experimental_features": { - "msc4108v2025_mode": "off", + "msc4388_mode": "off", }, } ) def test_off(self) -> None: - channel = self.make_request("POST", msc4108_endpoint, {}, access_token=None) + channel = self.make_request("POST", rz_endpoint, {}, access_token=None) self.assertEqual(channel.code, 404) @unittest.skip_unless(HAS_AUTHLIB, "requires authlib") @@ -145,7 +145,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): "endpoint": "https://issuer", }, "experimental_features": { - "msc4108v2025_mode": "public", + "msc4388_mode": "public", }, } ) @@ -161,7 +161,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # We can post arbitrary data to the endpoint channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=None, ) @@ -171,7 +171,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): expires_ts = channel.json_body["expires_ts"] self.assertGreater(expires_ts, self.hs.get_clock().time_msec()) - session_endpoint = msc4108_endpoint + f"/{rendezvous_id}" + session_endpoint = rz_endpoint + f"/{rendezvous_id}" # We can get the data back channel = self.make_request( @@ -207,7 +207,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): self.assertEqual(channel.code, 409) self.assertEqual( - channel.json_body["errcode"], "IO_ELEMENT_MSC4108_CONCURRENT_WRITE" + channel.json_body["errcode"], "IO_ELEMENT_MSC4388_CONCURRENT_WRITE" ) # We should get the updated data @@ -251,7 +251,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): "endpoint": "https://issuer", }, "experimental_features": { - "msc4108v2025_mode": "authenticated", + "msc4388_mode": "authenticated", }, } ) @@ -270,7 +270,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # This should fail without authentication: channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=None, ) @@ -279,7 +279,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # This should work as we are now authenticated channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=alice_token, ) @@ -289,7 +289,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): expires_ts = channel.json_body["expires_ts"] self.assertGreater(expires_ts, self.hs.get_clock().time_msec()) - session_endpoint = msc4108_endpoint + f"/{rendezvous_id}" + session_endpoint = rz_endpoint + f"/{rendezvous_id}" # We can get the data back without authentication channel = self.make_request( @@ -355,7 +355,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): "endpoint": "https://issuer", }, "experimental_features": { - "msc4108v2025_mode": "public", + "msc4388_mode": "public", }, } ) @@ -366,12 +366,12 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # Start a new session channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=None, ) self.assertEqual(channel.code, 200) - session_endpoint = msc4108_endpoint + "/" + channel.json_body["id"] + session_endpoint = rz_endpoint + "/" + channel.json_body["id"] # Sanity check that we can get the data back channel = self.make_request( @@ -403,7 +403,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): "endpoint": "https://issuer", }, "experimental_features": { - "msc4108v2025_mode": "public", + "msc4388_mode": "public", }, } ) @@ -415,12 +415,12 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # Start a new session channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=None, ) self.assertEqual(channel.code, 200) - session_endpoint = msc4108_endpoint + "/" + channel.json_body["id"] + session_endpoint = rz_endpoint + "/" + channel.json_body["id"] # Sanity check that we can get the data back channel = self.make_request( @@ -438,7 +438,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): for _ in range(100): channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=None, ) @@ -475,7 +475,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): "endpoint": "https://issuer", }, "experimental_features": { - "msc4108v2025_mode": "public", + "msc4388_mode": "public", }, } ) @@ -487,12 +487,12 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # Start a new session channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=None, ) self.assertEqual(channel.code, 200) - session_endpoint = msc4108_endpoint + "/" + channel.json_body["id"] + session_endpoint = rz_endpoint + "/" + channel.json_body["id"] # We advance the clock to make sure that this entry is the "lowest" in the session list self.reactor.advance(1) @@ -509,7 +509,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): for _ in range(200): channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "foo=bar"}, access_token=None, ) @@ -534,7 +534,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): "endpoint": "https://issuer", }, "experimental_features": { - "msc4108v2025_mode": "public", + "msc4388_mode": "public", }, } ) @@ -548,7 +548,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): for invalid_data in invalid_datas: channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": invalid_data}, access_token=None, ) @@ -558,7 +558,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # Make a valid request channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "test"}, access_token=None, ) @@ -566,7 +566,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): rendezvous_id = channel.json_body["id"] sequence_token = channel.json_body["sequence_token"] - session_endpoint = msc4108_endpoint + f"/{rendezvous_id}" + session_endpoint = rz_endpoint + f"/{rendezvous_id}" # We can't update the data with invalid data for invalid_data in invalid_datas: @@ -589,7 +589,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): "endpoint": "https://issuer", }, "experimental_features": { - "msc4108v2025_mode": "public", + "msc4388_mode": "public", }, } ) @@ -601,7 +601,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": too_long_data}, access_token=None, ) @@ -611,7 +611,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): # Make a valid request channel = self.make_request( "POST", - msc4108_endpoint, + rz_endpoint, {"data": "test"}, access_token=None, ) @@ -619,7 +619,7 @@ class RendezvousServletTestCase(unittest.HomeserverTestCase): rendezvous_id = channel.json_body["id"] sequence_token = channel.json_body["sequence_token"] - session_endpoint = msc4108_endpoint + f"/{rendezvous_id}" + session_endpoint = rz_endpoint + f"/{rendezvous_id}" # We can't update the data with invalid data channel = self.make_request(