diff --git a/changelog.d/19033.feature b/changelog.d/19033.feature new file mode 100644 index 00000000000..74042d98230 --- /dev/null +++ b/changelog.d/19033.feature @@ -0,0 +1 @@ +Stabilized support for [MSC4326](https://github.com/matrix-org/matrix-spec-proposals/pull/4326): Device masquerading for appservices. Contributed by @tulir @ Beeper. diff --git a/synapse/api/auth/base.py b/synapse/api/auth/base.py index 76c8c71628a..fd7d761f7d4 100644 --- a/synapse/api/auth/base.py +++ b/synapse/api/auth/base.py @@ -302,12 +302,9 @@ async def get_appservice_user( (the user_id URI parameter allows an application service to masquerade any applicable user in its namespace) - what device the application service should be treated as controlling - (the device_id[^1] URI parameter allows an application service to masquerade + (the device_id URI parameter allows an application service to masquerade as any device that exists for the relevant user) - [^1] Unstable and provided by MSC3202. - Must use `org.matrix.msc3202.device_id` in place of `device_id` for now. - Returns: the application service `Requester` of that request @@ -319,7 +316,8 @@ async def get_appservice_user( - The returned device ID, if present, has been checked to be a valid device ID for the returned user ID. """ - DEVICE_ID_ARG_NAME = b"org.matrix.msc3202.device_id" + # TODO: We can drop unstable support after 2026-01-01 (couple months after stable support) + UNSTABLE_DEVICE_ID_ARG_NAME = b"org.matrix.msc3202.device_id" app_service = self.store.get_app_service_by_token(access_token) if app_service is None: @@ -341,13 +339,11 @@ async def get_appservice_user( else: effective_user_id = app_service.sender - effective_device_id: Optional[str] = None - - if ( - self.hs.config.experimental.msc3202_device_masquerading_enabled - and DEVICE_ID_ARG_NAME in request.args - ): - effective_device_id = request.args[DEVICE_ID_ARG_NAME][0].decode("utf8") + effective_device_id_args = request.args.get( + b"device_id", request.args.get(UNSTABLE_DEVICE_ID_ARG_NAME) + ) + if effective_device_id_args: + effective_device_id = effective_device_id_args[0].decode("utf8") # We only just set this so it can't be None! assert effective_device_id is not None device_opt = await self.store.get_device( @@ -359,6 +355,8 @@ async def get_appservice_user( f"Application service trying to use a device that doesn't exist ('{effective_device_id}' for {effective_user_id})", Codes.UNKNOWN_DEVICE, ) + else: + effective_device_id = None return create_requester( effective_user_id, app_service=app_service, device_id=effective_device_id diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index 04ca6e3c517..f82e8572f22 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -412,11 +412,6 @@ def read_config( "msc2409_to_device_messages_enabled", False ) - # The portion of MSC3202 which is related to device masquerading. - self.msc3202_device_masquerading_enabled: bool = experimental.get( - "msc3202_device_masquerading", False - ) - # The portion of MSC3202 related to transaction extensions: # sending device list changes, one-time key counts and fallback key # usage to application services. diff --git a/tests/api/test_auth.py b/tests/api/test_auth.py index e7fcd928d71..f7905ced7e8 100644 --- a/tests/api/test_auth.py +++ b/tests/api/test_auth.py @@ -42,7 +42,6 @@ from synapse.util.clock import Clock from tests import unittest -from tests.unittest import override_config from tests.utils import mock_getRawHeaders @@ -237,7 +236,6 @@ def test_get_user_by_req_appservice_valid_token_bad_user_id(self) -> None: request.requestHeaders.getRawHeaders = mock_getRawHeaders() self.get_failure(self.auth.get_user_by_req(request), AuthError) - @override_config({"experimental_features": {"msc3202_device_masquerading": True}}) def test_get_user_by_req_appservice_valid_token_valid_device_id(self) -> None: """ Tests that when an application service passes the device_id URL parameter @@ -264,7 +262,7 @@ def test_get_user_by_req_appservice_valid_token_valid_device_id(self) -> None: request.getClientAddress.return_value.host = "127.0.0.1" request.args[b"access_token"] = [self.test_token] request.args[b"user_id"] = [masquerading_user_id] - request.args[b"org.matrix.msc3202.device_id"] = [masquerading_device_id] + request.args[b"device_id"] = [masquerading_device_id] request.requestHeaders.getRawHeaders = mock_getRawHeaders() requester = self.get_success(self.auth.get_user_by_req(request)) self.assertEqual( @@ -272,7 +270,6 @@ def test_get_user_by_req_appservice_valid_token_valid_device_id(self) -> None: ) self.assertEqual(requester.device_id, masquerading_device_id.decode("utf8")) - @override_config({"experimental_features": {"msc3202_device_masquerading": True}}) def test_get_user_by_req_appservice_valid_token_invalid_device_id(self) -> None: """ Tests that when an application service passes the device_id URL parameter @@ -299,7 +296,7 @@ def test_get_user_by_req_appservice_valid_token_invalid_device_id(self) -> None: request.getClientAddress.return_value.host = "127.0.0.1" request.args[b"access_token"] = [self.test_token] request.args[b"user_id"] = [masquerading_user_id] - request.args[b"org.matrix.msc3202.device_id"] = [masquerading_device_id] + request.args[b"device_id"] = [masquerading_device_id] request.requestHeaders.getRawHeaders = mock_getRawHeaders() failure = self.get_failure(self.auth.get_user_by_req(request), AuthError)