Add via param to hierarchy enpoint (#18070)
### Pull Request Checklist Implementation of [MSC4235](https://github.com/matrix-org/matrix-spec-proposals/pull/4235) as per suggestion in [pull request 17750](https://github.com/element-hq/synapse/pull/17750#issuecomment-2411248598). <!-- Please read https://element-hq.github.io/synapse/latest/development/contributing_guide.html before submitting your pull request --> * [x] Pull request is based on the develop branch * [x] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [x] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters)) --------- Co-authored-by: Quentin Gliech <quenting@element.io>
This commit is contained in:
1
changelog.d/18070.feature
Normal file
1
changelog.d/18070.feature
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Support for [MSC4235](https://github.com/matrix-org/matrix-spec-proposals/pull/4235): via query param for hierarchy endpoint. Contributed by Krishan (@kfiven).
|
||||||
@@ -561,6 +561,9 @@ class ExperimentalConfig(Config):
|
|||||||
# MSC4076: Add `disable_badge_count`` to pusher configuration
|
# MSC4076: Add `disable_badge_count`` to pusher configuration
|
||||||
self.msc4076_enabled: bool = experimental.get("msc4076_enabled", False)
|
self.msc4076_enabled: bool = experimental.get("msc4076_enabled", False)
|
||||||
|
|
||||||
|
# MSC4235: Add `via` param to hierarchy endpoint
|
||||||
|
self.msc4235_enabled: bool = experimental.get("msc4235_enabled", False)
|
||||||
|
|
||||||
# MSC4263: Preventing MXID enumeration via key queries
|
# MSC4263: Preventing MXID enumeration via key queries
|
||||||
self.msc4263_limit_key_queries_to_users_who_share_rooms = experimental.get(
|
self.msc4263_limit_key_queries_to_users_who_share_rooms = experimental.get(
|
||||||
"msc4263_limit_key_queries_to_users_who_share_rooms",
|
"msc4263_limit_key_queries_to_users_who_share_rooms",
|
||||||
|
|||||||
@@ -111,7 +111,15 @@ class RoomSummaryHandler:
|
|||||||
# If a user tries to fetch the same page multiple times in quick succession,
|
# If a user tries to fetch the same page multiple times in quick succession,
|
||||||
# only process the first attempt and return its result to subsequent requests.
|
# only process the first attempt and return its result to subsequent requests.
|
||||||
self._pagination_response_cache: ResponseCache[
|
self._pagination_response_cache: ResponseCache[
|
||||||
Tuple[str, str, bool, Optional[int], Optional[int], Optional[str]]
|
Tuple[
|
||||||
|
str,
|
||||||
|
str,
|
||||||
|
bool,
|
||||||
|
Optional[int],
|
||||||
|
Optional[int],
|
||||||
|
Optional[str],
|
||||||
|
Optional[Tuple[str, ...]],
|
||||||
|
]
|
||||||
] = ResponseCache(
|
] = ResponseCache(
|
||||||
hs.get_clock(),
|
hs.get_clock(),
|
||||||
"get_room_hierarchy",
|
"get_room_hierarchy",
|
||||||
@@ -126,6 +134,7 @@ class RoomSummaryHandler:
|
|||||||
max_depth: Optional[int] = None,
|
max_depth: Optional[int] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
from_token: Optional[str] = None,
|
from_token: Optional[str] = None,
|
||||||
|
remote_room_hosts: Optional[Tuple[str, ...]] = None,
|
||||||
) -> JsonDict:
|
) -> JsonDict:
|
||||||
"""
|
"""
|
||||||
Implementation of the room hierarchy C-S API.
|
Implementation of the room hierarchy C-S API.
|
||||||
@@ -143,6 +152,9 @@ class RoomSummaryHandler:
|
|||||||
limit: An optional limit on the number of rooms to return per
|
limit: An optional limit on the number of rooms to return per
|
||||||
page. Must be a positive integer.
|
page. Must be a positive integer.
|
||||||
from_token: An optional pagination token.
|
from_token: An optional pagination token.
|
||||||
|
remote_room_hosts: An optional list of remote homeserver server names. If defined,
|
||||||
|
each host will be used to try and fetch the room hierarchy. Must be a tuple so
|
||||||
|
that it can be hashed by the `RoomSummaryHandler._pagination_response_cache`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The JSON hierarchy dictionary.
|
The JSON hierarchy dictionary.
|
||||||
@@ -162,6 +174,7 @@ class RoomSummaryHandler:
|
|||||||
max_depth,
|
max_depth,
|
||||||
limit,
|
limit,
|
||||||
from_token,
|
from_token,
|
||||||
|
remote_room_hosts,
|
||||||
),
|
),
|
||||||
self._get_room_hierarchy,
|
self._get_room_hierarchy,
|
||||||
requester.user.to_string(),
|
requester.user.to_string(),
|
||||||
@@ -170,6 +183,7 @@ class RoomSummaryHandler:
|
|||||||
max_depth,
|
max_depth,
|
||||||
limit,
|
limit,
|
||||||
from_token,
|
from_token,
|
||||||
|
remote_room_hosts,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _get_room_hierarchy(
|
async def _get_room_hierarchy(
|
||||||
@@ -180,6 +194,7 @@ class RoomSummaryHandler:
|
|||||||
max_depth: Optional[int] = None,
|
max_depth: Optional[int] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
from_token: Optional[str] = None,
|
from_token: Optional[str] = None,
|
||||||
|
remote_room_hosts: Optional[Tuple[str, ...]] = None,
|
||||||
) -> JsonDict:
|
) -> JsonDict:
|
||||||
"""See docstring for SpaceSummaryHandler.get_room_hierarchy."""
|
"""See docstring for SpaceSummaryHandler.get_room_hierarchy."""
|
||||||
|
|
||||||
@@ -199,7 +214,7 @@ class RoomSummaryHandler:
|
|||||||
|
|
||||||
if not local_room:
|
if not local_room:
|
||||||
room_hierarchy = await self._summarize_remote_room_hierarchy(
|
room_hierarchy = await self._summarize_remote_room_hierarchy(
|
||||||
_RoomQueueEntry(requested_room_id, ()),
|
_RoomQueueEntry(requested_room_id, remote_room_hosts or ()),
|
||||||
False,
|
False,
|
||||||
)
|
)
|
||||||
root_room_entry = room_hierarchy[0]
|
root_room_entry = room_hierarchy[0]
|
||||||
@@ -240,7 +255,7 @@ class RoomSummaryHandler:
|
|||||||
processed_rooms = set(pagination_session["processed_rooms"])
|
processed_rooms = set(pagination_session["processed_rooms"])
|
||||||
else:
|
else:
|
||||||
# The queue of rooms to process, the next room is last on the stack.
|
# The queue of rooms to process, the next room is last on the stack.
|
||||||
room_queue = [_RoomQueueEntry(requested_room_id, ())]
|
room_queue = [_RoomQueueEntry(requested_room_id, remote_room_hosts or ())]
|
||||||
|
|
||||||
# Rooms we have already processed.
|
# Rooms we have already processed.
|
||||||
processed_rooms = set()
|
processed_rooms = set()
|
||||||
|
|||||||
@@ -1538,6 +1538,7 @@ class RoomHierarchyRestServlet(RestServlet):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self._auth = hs.get_auth()
|
self._auth = hs.get_auth()
|
||||||
self._room_summary_handler = hs.get_room_summary_handler()
|
self._room_summary_handler = hs.get_room_summary_handler()
|
||||||
|
self.msc4235_enabled = hs.config.experimental.msc4235_enabled
|
||||||
|
|
||||||
async def on_GET(
|
async def on_GET(
|
||||||
self, request: SynapseRequest, room_id: str
|
self, request: SynapseRequest, room_id: str
|
||||||
@@ -1547,6 +1548,15 @@ class RoomHierarchyRestServlet(RestServlet):
|
|||||||
max_depth = parse_integer(request, "max_depth")
|
max_depth = parse_integer(request, "max_depth")
|
||||||
limit = parse_integer(request, "limit")
|
limit = parse_integer(request, "limit")
|
||||||
|
|
||||||
|
# twisted.web.server.Request.args is incorrectly defined as Optional[Any]
|
||||||
|
remote_room_hosts = None
|
||||||
|
if self.msc4235_enabled:
|
||||||
|
args: Dict[bytes, List[bytes]] = request.args # type: ignore
|
||||||
|
via_param = parse_strings_from_args(
|
||||||
|
args, "org.matrix.msc4235.via", required=False
|
||||||
|
)
|
||||||
|
remote_room_hosts = tuple(via_param or [])
|
||||||
|
|
||||||
return 200, await self._room_summary_handler.get_room_hierarchy(
|
return 200, await self._room_summary_handler.get_room_hierarchy(
|
||||||
requester,
|
requester,
|
||||||
room_id,
|
room_id,
|
||||||
@@ -1554,6 +1564,7 @@ class RoomHierarchyRestServlet(RestServlet):
|
|||||||
max_depth=max_depth,
|
max_depth=max_depth,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
from_token=parse_string(request, "from"),
|
from_token=parse_string(request, "from"),
|
||||||
|
remote_room_hosts=remote_room_hosts,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1080,6 +1080,62 @@ class SpaceSummaryTestCase(unittest.HomeserverTestCase):
|
|||||||
self.assertEqual(federation_requests, 2)
|
self.assertEqual(federation_requests, 2)
|
||||||
self._assert_hierarchy(result, expected)
|
self._assert_hierarchy(result, expected)
|
||||||
|
|
||||||
|
def test_fed_remote_room_hosts(self) -> None:
|
||||||
|
"""
|
||||||
|
Test if requested room is available over federation using via's.
|
||||||
|
"""
|
||||||
|
fed_hostname = self.hs.hostname + "2"
|
||||||
|
fed_space = "#fed_space:" + fed_hostname
|
||||||
|
fed_subroom = "#fed_sub_room:" + fed_hostname
|
||||||
|
|
||||||
|
remote_room_hosts = tuple(fed_hostname)
|
||||||
|
|
||||||
|
requested_room_entry = _RoomEntry(
|
||||||
|
fed_space,
|
||||||
|
{
|
||||||
|
"room_id": fed_space,
|
||||||
|
"world_readable": True,
|
||||||
|
"join_rule": "public",
|
||||||
|
"room_type": RoomTypes.SPACE,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": EventTypes.SpaceChild,
|
||||||
|
"room_id": fed_space,
|
||||||
|
"state_key": fed_subroom,
|
||||||
|
"content": {"via": [fed_hostname]},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
child_room = {
|
||||||
|
"room_id": fed_subroom,
|
||||||
|
"world_readable": True,
|
||||||
|
"join_rule": "public",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def summarize_remote_room_hierarchy(
|
||||||
|
_self: Any, room: Any, suggested_only: bool
|
||||||
|
) -> Tuple[Optional[_RoomEntry], Dict[str, JsonDict], Set[str]]:
|
||||||
|
return requested_room_entry, {fed_subroom: child_room}, set()
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
(fed_space, [fed_subroom]),
|
||||||
|
(fed_subroom, ()),
|
||||||
|
]
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"synapse.handlers.room_summary.RoomSummaryHandler._summarize_remote_room_hierarchy",
|
||||||
|
new=summarize_remote_room_hierarchy,
|
||||||
|
):
|
||||||
|
result = self.get_success(
|
||||||
|
self.handler.get_room_hierarchy(
|
||||||
|
create_requester(self.user),
|
||||||
|
fed_space,
|
||||||
|
remote_room_hosts=remote_room_hosts,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._assert_hierarchy(result, expected)
|
||||||
|
|
||||||
|
|
||||||
class RoomSummaryTestCase(unittest.HomeserverTestCase):
|
class RoomSummaryTestCase(unittest.HomeserverTestCase):
|
||||||
servlets = [
|
servlets = [
|
||||||
|
|||||||
Reference in New Issue
Block a user