From 9ccf899229b76d14f0f9c8dee88f837caf778aaf Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 18 Feb 2020 12:31:47 +0000 Subject: [PATCH 1/5] initial impl of GET /_matrix/client/r0/rooms/{roomId}/aliases --- synapse/handlers/directory.py | 17 ++++++++++++++++- synapse/rest/client/v1/room.py | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py index 8c5980cb0cb0..203d52db639f 100644 --- a/synapse/handlers/directory.py +++ b/synapse/handlers/directory.py @@ -16,6 +16,7 @@ import logging import string +from typing import List from twisted.internet import defer @@ -28,7 +29,7 @@ StoreError, SynapseError, ) -from synapse.types import RoomAlias, UserID, get_domain_from_id +from synapse.types import Requester, RoomAlias, UserID, get_domain_from_id from ._base import BaseHandler @@ -447,3 +448,17 @@ def edit_published_appservice_room_list( yield self.store.set_room_is_public_appservice( room_id, appservice_id, network_id, visibility == "public" ) + + async def get_aliases_for_room( + self, requester: Requester, room_id: str + ) -> List[str]: + """ + Get a list of the aliases that currently point to this room on this server + """ + # allow access to server admins and current members of the room + is_admin = await self.auth.is_server_admin(requester.user) + if not is_admin: + await self.auth.check_joined_room(room_id, requester.user.to_string) + + aliases = await self.store.get_aliases_for_room(room_id) + return aliases diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py index 6f31584c51d4..00969aa31ddf 100644 --- a/synapse/rest/client/v1/room.py +++ b/synapse/rest/client/v1/room.py @@ -45,6 +45,10 @@ from synapse.streams.config import PaginationConfig from synapse.types import RoomAlias, RoomID, StreamToken, ThirdPartyInstanceID, UserID +MYPY = False +if MYPY: + import synapse.server + logger = logging.getLogger(__name__) @@ -843,6 +847,24 @@ async def on_PUT(self, request, room_id, user_id): return 200, {} +class RoomAliasListServlet(RestServlet): + PATTERNS = client_patterns("/rooms/(?P[^/]*)/aliases", unstable=False) + + def __init__(self, hs: "synapse.server.HomeServer"): + super().__init__() + self.auth = hs.get_auth() + self.directory_handler = hs.get_handlers().directory_handler + + async def on_GET(self, request, room_id): + requester = await self.auth.get_user_by_req(request) + + alias_list = await self.directory_handler.get_aliases_for_room( + requester, room_id + ) + + return 200, {"aliases": alias_list} + + class SearchRestServlet(RestServlet): PATTERNS = client_patterns("/search$", v1=True) From b3131d661397dd29fffbeadd9cfc76465b7b739f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 18 Feb 2020 13:08:17 +0000 Subject: [PATCH 2/5] changelog --- changelog.d/6939.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6939.feature diff --git a/changelog.d/6939.feature b/changelog.d/6939.feature new file mode 100644 index 000000000000..40fe7fc9a94e --- /dev/null +++ b/changelog.d/6939.feature @@ -0,0 +1 @@ +Implement `GET /_matrix/client/r0/rooms/{roomId}/aliases` endpoint as per [MSC2432](https://github.com/matrix-org/matrix-doc/pull/2432). From 45972cd6456ec7bba8d8617b1be7490ff5ff9752 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 18 Feb 2020 13:12:51 +0000 Subject: [PATCH 3/5] fix bugs --- synapse/handlers/directory.py | 2 +- synapse/rest/client/v1/room.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py index 203d52db639f..972316c7cd7c 100644 --- a/synapse/handlers/directory.py +++ b/synapse/handlers/directory.py @@ -458,7 +458,7 @@ async def get_aliases_for_room( # allow access to server admins and current members of the room is_admin = await self.auth.is_server_admin(requester.user) if not is_admin: - await self.auth.check_joined_room(room_id, requester.user.to_string) + await self.auth.check_joined_room(room_id, requester.user.to_string()) aliases = await self.store.get_aliases_for_room(room_id) return aliases diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py index 00969aa31ddf..143dc738c670 100644 --- a/synapse/rest/client/v1/room.py +++ b/synapse/rest/client/v1/room.py @@ -953,6 +953,7 @@ def register_servlets(hs, http_server): JoinedRoomsRestServlet(hs).register(http_server) RoomEventServlet(hs).register(http_server) RoomEventContextServlet(hs).register(http_server) + RoomAliasListServlet(hs).register(http_server) def register_deprecated_servlets(hs, http_server): From 5f46fb912780a22b37afd483387d9b073d4e42ef Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 18 Feb 2020 14:07:43 +0000 Subject: [PATCH 4/5] unit tests for /room/aliases --- tests/rest/client/v1/test_rooms.py | 70 +++++++++++++++++++++++++++++- tests/unittest.py | 29 ++++++++----- 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/tests/rest/client/v1/test_rooms.py b/tests/rest/client/v1/test_rooms.py index fb681a1db98a..fb08a45d2705 100644 --- a/tests/rest/client/v1/test_rooms.py +++ b/tests/rest/client/v1/test_rooms.py @@ -28,8 +28,9 @@ import synapse.rest.admin from synapse.api.constants import EventContentFields, EventTypes, Membership from synapse.handlers.pagination import PurgeStatus -from synapse.rest.client.v1 import login, profile, room +from synapse.rest.client.v1 import directory, login, profile, room from synapse.rest.client.v2_alpha import account +from synapse.types import JsonDict, RoomAlias from synapse.util.stringutils import random_string from tests import unittest @@ -1726,3 +1727,70 @@ def test_erased_sender(self): self.assertEqual(len(events_after), 2, events_after) self.assertDictEqual(events_after[0].get("content"), {}, events_after[0]) self.assertEqual(events_after[1].get("content"), {}, events_after[1]) + + +class DirectoryTestCase(unittest.HomeserverTestCase): + + servlets = [ + synapse.rest.admin.register_servlets_for_client_rest_resource, + directory.register_servlets, + login.register_servlets, + room.register_servlets, + ] + + def prepare(self, reactor, clock, homeserver): + self.room_owner = self.register_user("room_owner", "test") + self.room_owner_tok = self.login("room_owner", "test") + + self.room_id = self.helper.create_room_as( + self.room_owner, tok=self.room_owner_tok + ) + + def test_no_aliases(self): + res = self._get_aliases(self.room_owner_tok) + self.assertEqual(res["aliases"], []) + + def test_not_in_room(self): + self.register_user("user", "test") + user_tok = self.login("user", "test") + res = self._get_aliases(user_tok, expected_code=403) + self.assertEqual(res["errcode"], "M_FORBIDDEN") + + def test_with_aliases(self): + alias1 = self._random_alias() + alias2 = self._random_alias() + + self._set_alias_via_directory(alias1) + self._set_alias_via_directory(alias2) + + res = self._get_aliases(self.room_owner_tok) + self.assertEqual(set(res["aliases"]), {alias1, alias2}) + + def _get_aliases(self, access_token: str, expected_code: int = 200) -> JsonDict: + """Calls the endpoint under test. returns the json response object.""" + request, channel = self.make_request( + "GET", + "/_matrix/client/r0/rooms/%s/aliases" % (self.room_id,), + access_token=access_token, + ) + self.render(request) + self.assertEqual(channel.code, expected_code, channel.result) + res = channel.json_body + self.assertIsInstance(res, dict) + if expected_code == 200: + self.assertIsInstance(res["aliases"], list) + return res + + def _random_alias(self) -> str: + return RoomAlias(random_string(5), self.hs.hostname).to_string() + + def _set_alias_via_directory(self, alias: str, expected_code: int = 200): + url = "/_matrix/client/r0/directory/room/" + alias + data = {"room_id": self.room_id} + request_data = json.dumps(data) + + request, channel = self.make_request( + "PUT", url, request_data, access_token=self.room_owner_tok + ) + self.render(request) + self.assertEqual(channel.code, expected_code, channel.result) diff --git a/tests/unittest.py b/tests/unittest.py index 98bf27d39cd8..9f626a9f0110 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -21,6 +21,7 @@ import inspect import logging import time +from typing import Optional, Tuple, TypeVar, Union from mock import Mock @@ -42,7 +43,13 @@ from synapse.types import Requester, UserID, create_requester from synapse.util.ratelimitutils import FederationRateLimiter -from tests.server import get_clock, make_request, render, setup_test_homeserver +from tests.server import ( + FakeChannel, + get_clock, + make_request, + render, + setup_test_homeserver, +) from tests.test_utils.logging_setup import setup_logging from tests.utils import default_config, setupdb @@ -334,14 +341,13 @@ def prepare(self, reactor, clock, homeserver): def make_request( self, - method, - path, - content=b"", - access_token=None, - request=SynapseRequest, - shorthand=True, - federation_auth_origin=None, - ): + method: Union[bytes, str], + path: Union[bytes, str], + content: Union[bytes, dict] = b"", + access_token: Optional[str] = None, + shorthand: bool = True, + federation_auth_origin: str = None, + ) -> Tuple[SynapseRequest, FakeChannel]: """ Create a SynapseRequest at the path using the method and containing the given content. @@ -369,9 +375,8 @@ def make_request( path, content, access_token, - request, - shorthand, - federation_auth_origin, + shorthand=shorthand, + federation_auth_origin=federation_auth_origin, ) def render(self, request): From c640209cb67b2d3eaf7b7337f913b3ea88028c70 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 18 Feb 2020 14:17:51 +0000 Subject: [PATCH 5/5] fix test fail --- tests/unittest.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/unittest.py b/tests/unittest.py index 9f626a9f0110..8816a4d152ca 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -21,7 +21,7 @@ import inspect import logging import time -from typing import Optional, Tuple, TypeVar, Union +from typing import Optional, Tuple, Type, TypeVar, Union from mock import Mock @@ -78,6 +78,9 @@ def new(*args, **kwargs): return _around +T = TypeVar("T") + + class TestCase(unittest.TestCase): """A subclass of twisted.trial's TestCase which looks for 'loglevel' attributes on both itself and its individual test methods, to override the @@ -345,9 +348,10 @@ def make_request( path: Union[bytes, str], content: Union[bytes, dict] = b"", access_token: Optional[str] = None, + request: Type[T] = SynapseRequest, shorthand: bool = True, federation_auth_origin: str = None, - ) -> Tuple[SynapseRequest, FakeChannel]: + ) -> Tuple[T, FakeChannel]: """ Create a SynapseRequest at the path using the method and containing the given content. @@ -375,8 +379,9 @@ def make_request( path, content, access_token, - shorthand=shorthand, - federation_auth_origin=federation_auth_origin, + request, + shorthand, + federation_auth_origin, ) def render(self, request):