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

Commit

Permalink
Admin API for querying rooms where a user is a member (#8306)
Browse files Browse the repository at this point in the history
Add a new admin API `GET /_synapse/admin/v1/users/<user_id>/joined_rooms` to
list all rooms where a user is a member.
  • Loading branch information
dklimpel authored Sep 18, 2020
1 parent 36efbca commit d688b4b
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 2 deletions.
1 change: 1 addition & 0 deletions changelog.d/8306.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add an admin API for querying rooms where a user is a member. Contributed by @dklimpel.
37 changes: 37 additions & 0 deletions docs/admin_api/user_admin_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,43 @@ To use it, you will need to authenticate by providing an ``access_token`` for a
server admin: see `README.rst <README.rst>`_.


List room memberships of an user
================================
Gets a list of all ``room_id`` that a specific ``user_id`` is member.

The API is::

GET /_synapse/admin/v1/users/<user_id>/joined_rooms

To use it, you will need to authenticate by providing an ``access_token`` for a
server admin: see `README.rst <README.rst>`_.

A response body like the following is returned:

.. code:: json
{
"joined_rooms": [
"!DuGcnbhHGaSZQoNQR:matrix.org",
"!ZtSaPCawyWtxfWiIy:matrix.org"
],
"total": 2
}
**Parameters**

The following parameters should be set in the URL:

- ``user_id`` - fully qualified: for example, ``@user:server.com``.

**Response**

The following fields are returned in the JSON response body:

- ``joined_rooms`` - An array of ``room_id``.
- ``total`` - Number of rooms.


User devices
============

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 @@ -49,6 +49,7 @@
ResetPasswordRestServlet,
SearchUsersRestServlet,
UserAdminServlet,
UserMembershipRestServlet,
UserRegisterServlet,
UserRestServletV2,
UsersRestServlet,
Expand Down Expand Up @@ -209,6 +210,7 @@ def register_servlets(hs, http_server):
SendServerNoticeServlet(hs).register(http_server)
VersionServlet(hs).register(http_server)
UserAdminServlet(hs).register(http_server)
UserMembershipRestServlet(hs).register(http_server)
UserRestServletV2(hs).register(http_server)
UsersRestServletV2(hs).register(http_server)
DeviceRestServlet(hs).register(http_server)
Expand Down
26 changes: 26 additions & 0 deletions synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,29 @@ async def on_PUT(self, request, user_id):
await self.store.set_server_admin(target_user, set_admin_to)

return 200, {}


class UserMembershipRestServlet(RestServlet):
"""
Get room list of an user.
"""

PATTERNS = admin_patterns("/users/(?P<user_id>[^/]+)/joined_rooms$")

def __init__(self, hs):
self.is_mine = hs.is_mine
self.auth = hs.get_auth()
self.store = hs.get_datastore()

async def on_GET(self, request, user_id):
await assert_requester_is_admin(self.auth, request)

if not self.is_mine(UserID.from_string(user_id)):
raise SynapseError(400, "Can only lookup local users")

room_ids = await self.store.get_rooms_for_user(user_id)
if not room_ids:
raise NotFoundError("User not found")

ret = {"joined_rooms": list(room_ids), "total": len(room_ids)}
return 200, ret
96 changes: 94 additions & 2 deletions tests/rest/admin/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

import synapse.rest.admin
from synapse.api.constants import UserTypes
from synapse.api.errors import HttpResponseException, ResourceLimitError
from synapse.rest.client.v1 import login
from synapse.api.errors import Codes, HttpResponseException, ResourceLimitError
from synapse.rest.client.v1 import login, room
from synapse.rest.client.v2_alpha import sync

from tests import unittest
Expand Down Expand Up @@ -995,3 +995,95 @@ def test_accidental_deactivation_prevention(self):

# Ensure they're still alive
self.assertEqual(0, channel.json_body["deactivated"])


class UserMembershipRestTestCase(unittest.HomeserverTestCase):

servlets = [
synapse.rest.admin.register_servlets,
login.register_servlets,
sync.register_servlets,
room.register_servlets,
]

def prepare(self, reactor, clock, hs):
self.store = hs.get_datastore()

self.admin_user = self.register_user("admin", "pass", admin=True)
self.admin_user_tok = self.login("admin", "pass")

self.other_user = self.register_user("user", "pass")
self.url = "/_synapse/admin/v1/users/%s/joined_rooms" % urllib.parse.quote(
self.other_user
)

def test_no_auth(self):
"""
Try to list rooms of an user without authentication.
"""
request, channel = self.make_request("GET", self.url, b"{}")
self.render(request)

self.assertEqual(401, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"])

def test_requester_is_no_admin(self):
"""
If the user is not a server admin, an error is returned.
"""
other_user_token = self.login("user", "pass")

request, channel = self.make_request(
"GET", self.url, access_token=other_user_token,
)
self.render(request)

self.assertEqual(403, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"])

def test_user_does_not_exist(self):
"""
Tests that a lookup for a user that does not exist returns a 404
"""
url = "/_synapse/admin/v1/users/@unknown_person:test/joined_rooms"
request, channel = self.make_request(
"GET", url, access_token=self.admin_user_tok,
)
self.render(request)

self.assertEqual(404, channel.code, msg=channel.json_body)
self.assertEqual(Codes.NOT_FOUND, channel.json_body["errcode"])

def test_user_is_not_local(self):
"""
Tests that a lookup for a user that is not a local returns a 400
"""
url = "/_synapse/admin/v1/users/@unknown_person:unknown_domain/joined_rooms"

request, channel = self.make_request(
"GET", url, access_token=self.admin_user_tok,
)
self.render(request)

self.assertEqual(400, channel.code, msg=channel.json_body)
self.assertEqual("Can only lookup local users", channel.json_body["error"])

def test_get_rooms(self):
"""
Tests that a normal lookup for rooms is successfully
"""
# Create rooms and join
other_user_tok = self.login("user", "pass")
number_rooms = 5
for n in range(number_rooms):
self.helper.create_room_as(self.other_user, tok=other_user_tok)

# Get rooms
request, channel = self.make_request(
"GET", self.url, access_token=self.admin_user_tok,
)
self.render(request)

self.assertEqual(200, channel.code, msg=channel.json_body)
self.assertEqual(number_rooms, channel.json_body["total"])
self.assertEqual(number_rooms, len(channel.json_body["joined_rooms"]))

0 comments on commit d688b4b

Please sign in to comment.