Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add an admin API to get the current room state #9168

Merged
merged 4 commits into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/9168.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add an admin API for retrieving the current room state of a room.
30 changes: 30 additions & 0 deletions docs/admin_api/rooms.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,36 @@ Response:
}
```

# Room State API

The Room State admin API allows server admins to get a list of all state events in a room.

The response includes the following fields:

* `state` - The current state of the room at the time of request.

## Usage

A standard request:

```
GET /_synapse/admin/v1/rooms/<room_id>/state

{}
```

Response:

```json
{
"state": [
{"type": "m.room.create", "state_key": "", "etc": true},
{"type": "m.room.power_levels", "state_key": "", "etc": true},
{"type": "m.room.name", "state_key": "", "etc": true}
]
}
```

# Delete Room API

The Delete Room admin API allows server admins to remove rooms from server
Expand Down
13 changes: 9 additions & 4 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ async def get_state_events(
state_filter: StateFilter = StateFilter.all(),
at_token: Optional[StreamToken] = None,
is_guest: bool = False,
is_admin: bool = False,
) -> List[dict]:
"""Retrieve all state events for a given room. If the user is
joined to the room then return the current state. If the user has
Expand All @@ -153,6 +154,7 @@ async def get_state_events(
stream token, we raise a 403 SynapseError. If None, returns the current
state based on the current_state_events table.
is_guest: whether this user is a guest
is_admin: whether this user is making the request as a server admin.
Returns:
A list of dicts representing state events. [{}, {}, {}]
Raises:
Expand All @@ -173,9 +175,12 @@ async def get_state_events(
if not last_events:
raise NotFoundError("Can't find event for token %s" % (at_token,))

visible_events = await filter_events_for_client(
self.storage, user_id, last_events, filter_send_to_client=False
)
if is_admin:
visible_events = last_events
else:
visible_events = await filter_events_for_client(
self.storage, user_id, last_events, filter_send_to_client=False,
)

event = last_events[0]
if visible_events:
Expand All @@ -197,7 +202,7 @@ async def get_state_events(
room_id, user_id, allow_departed_users=True
)

if membership == Membership.JOIN:
if membership == Membership.JOIN or is_admin:
state_ids = await self.store.get_filtered_current_state_ids(
room_id, state_filter=state_filter
)
Expand Down
2 changes: 2 additions & 0 deletions synapse/rest/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
MakeRoomAdminRestServlet,
RoomMembersRestServlet,
RoomRestServlet,
RoomStateRestServlet,
ShutdownRoomRestServlet,
)
from synapse.rest.admin.server_notice_servlet import SendServerNoticeServlet
Expand Down Expand Up @@ -209,6 +210,7 @@ def register_servlets(hs, http_server):
"""
register_servlets_for_client_rest_resource(hs, http_server)
ListRoomRestServlet(hs).register(http_server)
RoomStateRestServlet(hs).register(http_server)
RoomRestServlet(hs).register(http_server)
RoomMembersRestServlet(hs).register(http_server)
DeleteRoomRestServlet(hs).register(http_server)
Expand Down
33 changes: 33 additions & 0 deletions synapse/rest/admin/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,39 @@ async def on_GET(
return 200, ret


class RoomStateRestServlet(RestServlet):
"""
Get full state within a room.
"""

PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]+)/state")

def __init__(self, hs: "HomeServer"):
self.hs = hs
self.auth = hs.get_auth()
self.store = hs.get_datastore()
self.message_handler = hs.get_message_handler()

async def on_GET(
self, request: SynapseRequest, room_id: str
) -> Tuple[int, JsonDict]:
requester = await self.auth.get_user_by_req(request)
await assert_user_is_admin(self.auth, requester.user)

ret = await self.store.get_room(room_id)
if not ret:
raise NotFoundError("Room not found")

room_state = await self.message_handler.get_state_events(
user_id=requester.user.to_string(),
room_id=room_id,
is_admin=True, # already verified above
)
turt2live marked this conversation as resolved.
Show resolved Hide resolved
ret = {"state": room_state}

return 200, ret


class JoinRoomAliasServlet(RestServlet):

PATTERNS = admin_patterns("/join/(?P<room_identifier>[^/]*)")
Expand Down
15 changes: 15 additions & 0 deletions tests/rest/admin/test_room.py
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,21 @@ def test_room_members(self):
)
self.assertEqual(channel.json_body["total"], 3)

def test_room_state(self):
"""Test that room state can be requested correctly"""
# Create two test rooms
room_id = self.helper.create_room_as(self.admin_user, tok=self.admin_user_tok)

url = "/_synapse/admin/v1/rooms/%s/state" % (room_id,)
channel = self.make_request(
"GET", url.encode("ascii"), access_token=self.admin_user_tok,
)
self.assertEqual(200, channel.code, msg=channel.json_body)
self.assertIn("state", channel.json_body)
# testing that the state events match is painful and not done here. We assume that
# the create_room already does the right thing, so no need to verify that we got
# the state events it created.


class JoinAliasRoomTestCase(unittest.HomeserverTestCase):

Expand Down