From 7d4736aa80d44a5a87ff9c2e00c3c2fe0ef3172b Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Jul 2024 15:23:37 +0100 Subject: [PATCH 1/3] Allow enabling sliding sync per-user --- synapse/api/auth/__init__.py | 18 ++++++++++++- synapse/api/auth/internal.py | 29 +++++++++++++++++++++ synapse/api/auth/msc3861_delegated.py | 28 ++++++++++++++++++++ synapse/rest/admin/experimental_features.py | 3 +++ synapse/rest/client/sync.py | 15 +++++++---- 5 files changed, 87 insertions(+), 6 deletions(-) diff --git a/synapse/api/auth/__init__.py b/synapse/api/auth/__init__.py index 234dcf1ca41..d5241afe730 100644 --- a/synapse/api/auth/__init__.py +++ b/synapse/api/auth/__init__.py @@ -18,7 +18,7 @@ # [This file includes modifications made by New Vector Limited] # # -from typing import Optional, Tuple +from typing import TYPE_CHECKING, Optional, Tuple from typing_extensions import Protocol @@ -28,6 +28,9 @@ from synapse.http.site import SynapseRequest from synapse.types import Requester +if TYPE_CHECKING: + from synapse.rest.admin.experimental_features import ExperimentalFeature + # guests always get this device id. GUEST_DEVICE_ID = "guest_device" @@ -87,6 +90,19 @@ async def get_user_by_req( AuthError if access is denied for the user in the access token """ + async def get_user_by_req_experimental_feature( + self, + request: SynapseRequest, + feature: "ExperimentalFeature", + allow_guest: bool = False, + allow_expired: bool = False, + allow_locked: bool = False, + ) -> Requester: + """Like `get_user_by_req`, except also checks if the user has access to + the experimental feature. If they don't returns a 404 unrecognized + request. + """ + async def validate_appservice_can_control_user_id( self, app_service: ApplicationService, user_id: str ) -> None: diff --git a/synapse/api/auth/internal.py b/synapse/api/auth/internal.py index 2878f3e6e92..9fd4db68e15 100644 --- a/synapse/api/auth/internal.py +++ b/synapse/api/auth/internal.py @@ -28,6 +28,7 @@ Codes, InvalidClientTokenError, MissingClientTokenError, + UnrecognizedRequestError, ) from synapse.http.site import SynapseRequest from synapse.logging.opentracing import active_span, force_tracing, start_active_span @@ -38,8 +39,10 @@ from .base import BaseAuth if TYPE_CHECKING: + from synapse.rest.admin.experimental_features import ExperimentalFeature from synapse.server import HomeServer + logger = logging.getLogger(__name__) @@ -106,6 +109,32 @@ async def get_user_by_req( parent_span.set_tag("appservice_id", requester.app_service.id) return requester + async def get_user_by_req_experimental_feature( + self, + request: SynapseRequest, + feature: "ExperimentalFeature", + allow_guest: bool = False, + allow_expired: bool = False, + allow_locked: bool = False, + ) -> Requester: + try: + requester = await self.get_user_by_req( + request, + allow_guest=allow_guest, + allow_expired=allow_expired, + allow_locked=allow_locked, + ) + if await self.store.is_feature_enabled(requester.user.to_string(), feature): + return requester + + raise UnrecognizedRequestError(code=404) + except (AuthError, InvalidClientTokenError): + if feature.is_globally_enabled(self.hs.config): + # If its globally enabled then return the auth error + raise + + raise UnrecognizedRequestError(code=404) + @cancellable async def _wrapped_get_user_by_req( self, diff --git a/synapse/api/auth/msc3861_delegated.py b/synapse/api/auth/msc3861_delegated.py index 3146e1577c3..f61b39ded7c 100644 --- a/synapse/api/auth/msc3861_delegated.py +++ b/synapse/api/auth/msc3861_delegated.py @@ -40,6 +40,7 @@ OAuthInsufficientScopeError, StoreError, SynapseError, + UnrecognizedRequestError, ) from synapse.http.site import SynapseRequest from synapse.logging.context import make_deferred_yieldable @@ -48,6 +49,7 @@ from synapse.util.caches.cached_call import RetryOnExceptionCachedCall if TYPE_CHECKING: + from synapse.rest.admin.experimental_features import ExperimentalFeature from synapse.server import HomeServer logger = logging.getLogger(__name__) @@ -245,6 +247,32 @@ async def get_user_by_req( return requester + async def get_user_by_req_experimental_feature( + self, + request: SynapseRequest, + feature: "ExperimentalFeature", + allow_guest: bool = False, + allow_expired: bool = False, + allow_locked: bool = False, + ) -> Requester: + try: + requester = await self.get_user_by_req( + request, + allow_guest=allow_guest, + allow_expired=allow_expired, + allow_locked=allow_locked, + ) + if await self.store.is_feature_enabled(requester.user.to_string(), feature): + return requester + + raise UnrecognizedRequestError(code=404) + except (AuthError, InvalidClientTokenError): + if feature.is_globally_enabled(self.hs.config): + # If its globally enabled then return the auth error + raise + + raise UnrecognizedRequestError(code=404) + async def get_user_by_access_token( self, token: str, diff --git a/synapse/rest/admin/experimental_features.py b/synapse/rest/admin/experimental_features.py index c1559c92f7d..d7913896d91 100644 --- a/synapse/rest/admin/experimental_features.py +++ b/synapse/rest/admin/experimental_features.py @@ -42,10 +42,13 @@ class ExperimentalFeature(str, Enum): """ MSC3881 = "msc3881" + MSC3575 = "msc3575" def is_globally_enabled(self, config: "HomeServerConfig") -> bool: if self is ExperimentalFeature.MSC3881: return config.experimental.msc3881_enabled + if self is ExperimentalFeature.MSC3575: + return config.experimental.msc3575_enabled assert_never(self) diff --git a/synapse/rest/client/sync.py b/synapse/rest/client/sync.py index e52e771538b..2a22bc14ecb 100644 --- a/synapse/rest/client/sync.py +++ b/synapse/rest/client/sync.py @@ -53,6 +53,7 @@ ) from synapse.http.site import SynapseRequest from synapse.logging.opentracing import trace_with_opname +from synapse.rest.admin.experimental_features import ExperimentalFeature from synapse.types import JsonDict, Requester, StreamToken from synapse.types.rest.client import SlidingSyncBody from synapse.util import json_decoder @@ -673,7 +674,9 @@ def __init__(self, hs: "HomeServer"): ) async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: - requester = await self.auth.get_user_by_req(request, allow_guest=True) + requester = await self.auth.get_user_by_req_experimental_feature( + request, allow_guest=True, feature=ExperimentalFeature.MSC3575 + ) user = requester.user device_id = requester.device_id @@ -873,7 +876,10 @@ def __init__(self, hs: "HomeServer"): self.event_serializer = hs.get_event_client_serializer() async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: - requester = await self.auth.get_user_by_req(request, allow_guest=True) + requester = await self.auth.get_user_by_req_experimental_feature( + request, allow_guest=True, feature=ExperimentalFeature.MSC3575 + ) + user = requester.user device_id = requester.device_id @@ -1051,6 +1057,5 @@ async def encode_rooms( def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: SyncRestServlet(hs).register(http_server) - if hs.config.experimental.msc3575_enabled: - SlidingSyncRestServlet(hs).register(http_server) - SlidingSyncE2eeRestServlet(hs).register(http_server) + SlidingSyncRestServlet(hs).register(http_server) + SlidingSyncE2eeRestServlet(hs).register(http_server) From f19a202417d868cc841fff5be3db847500aaed1e Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Jul 2024 15:24:17 +0100 Subject: [PATCH 2/3] Newsfile --- changelog.d/17393.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/17393.misc diff --git a/changelog.d/17393.misc b/changelog.d/17393.misc new file mode 100644 index 00000000000..e1312252769 --- /dev/null +++ b/changelog.d/17393.misc @@ -0,0 +1 @@ +Allow enabling sliding sync per-user. From ca795393d1c16741867dbd0bf86645cd62194b9d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 5 Jul 2024 13:04:06 +0100 Subject: [PATCH 3/3] Update docs --- docs/admin_api/experimental_features.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/admin_api/experimental_features.md b/docs/admin_api/experimental_features.md index 250cfc13a3b..ef1b58c9ba0 100644 --- a/docs/admin_api/experimental_features.md +++ b/docs/admin_api/experimental_features.md @@ -4,6 +4,7 @@ This API allows a server administrator to enable or disable some experimental fe basis. The currently supported features are: - [MSC3881](https://github.com/matrix-org/matrix-spec-proposals/pull/3881): enable remotely toggling push notifications for another client +- [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575): enable experimental sliding sync support To use it, you will need to authenticate by providing an `access_token` for a server admin: see [Admin API](../usage/administration/admin_api/).