Skip to content

Commit c922b57

Browse files
author
Pavel Kardash
committed
Initial Admin API implementation
1 parent 9a3594a commit c922b57

File tree

2 files changed

+285
-0
lines changed

2 files changed

+285
-0
lines changed

matrix_client/admin_api.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# -*- coding: utf-8 -*-
2+
from matrix_client.api import MatrixHttpApi
3+
try:
4+
basestring
5+
except NameError:
6+
basestring = str
7+
8+
9+
class MatrixHttpAdminApi(MatrixHttpApi):
10+
"""Extends Matrix API with admin calls.
11+
12+
Examples:
13+
Create a client and send a message::
14+
15+
matrix = MatrixHttpAdminApi("https://matrix.org", token="foobar")
16+
response = admin_api.shutdown_room(
17+
"!DgvjtOljKujDBrxyHk:matrix.org",
18+
"@admin:matrix.org",
19+
room_name="New room",
20+
message="Old room closed by admin"
21+
)
22+
"""
23+
def purge_history(self, room_id, event_id):
24+
"""Perform /admin/purge_hostory.
25+
Admin api part.
26+
Args:
27+
room_id (str): Room_id to purge.
28+
event_id (str or int): Event_id or ts to purge before.
29+
"""
30+
if isinstance(event_id, basestring):
31+
content = {
32+
"delete_local_events": True,
33+
"purge_up_to_event_id": event_id
34+
}
35+
else:
36+
content = {
37+
"delete_local_events": True,
38+
"purge_up_to_ts": int(event_id)
39+
}
40+
return self._send("POST", "/admin/purge_history/%s" % room_id, content)
41+
42+
def purge_history_status(self, purge_id):
43+
"""Perform /admin/purge_history_status.
44+
Admin api part.
45+
Args:
46+
purge_id (str): Purge_id to query status.
47+
"""
48+
return self._send("GET", "/admin/purge_history_status/%s" % purge_id)
49+
50+
def media_in_room(self, room_id, event_id=None):
51+
"""List remote and local media in room.
52+
Args:
53+
room_id (str): Room_id to purge.
54+
"""
55+
return self._send("GET", "/admin/room/%s/media" % room_id)
56+
57+
def whois(self, user_id):
58+
"""Query server for user information (ip, UA, last seen).
59+
Admin api part.
60+
Args:
61+
user_id (str): user_id to query.
62+
"""
63+
return self._send("GET", "/admin/whois/%s" % user_id)
64+
65+
def deactivate(self, user_id, erase=False):
66+
"""Deactivate user account.
67+
Admin api part.
68+
Args:
69+
user_id (str): user_id to deactivate.
70+
erase (bool): erase user data. Default no.
71+
"""
72+
content = {
73+
"erase": erase
74+
}
75+
return self._send("POST", "/admin/deactivate/%s" % user_id, content)
76+
77+
def reset_password(self, user_id, password):
78+
"""Reset users's password to provided.
79+
Admin api part.
80+
Args:
81+
user_id (str): user_id to deactivate.
82+
password (str): password to set.
83+
"""
84+
content = {
85+
"new_password": password
86+
}
87+
return self._send("POST", "/admin/reset_password/%s" % user_id, content)
88+
89+
def quarantine_media(self, room_id):
90+
"""Quarantine all media in room so that no one can download it via thi server.
91+
Admin api part.
92+
Args:
93+
room_id (str): room_id to quarantine.
94+
"""
95+
return self._send("POST", "/admin/quarantine_media/%s" % room_id)
96+
97+
def shutdown_room(self, room_id, new_room_user_id, room_name=False, message=False):
98+
"""Shuts down a room by removing all local users from the room and blocking
99+
all future invites and joins to the room. Any local aliases will be repointed
100+
to a new room created by `new_room_user_id` and kicked users will be auto
101+
joined to the new room
102+
Admin api part.
103+
Args:
104+
room_id (str): room_id to quarantine.
105+
new_room_user_id (str): new room creator user_id.
106+
room_name (str): new room name.
107+
message (str): information message for new room.
108+
"""
109+
content = {
110+
"new_room_user_id": new_room_user_id
111+
}
112+
if room_name:
113+
content["room_name"] = room_name
114+
if message:
115+
content["message"] = message
116+
return self._send("POST", "/admin/shutdown_room/%s" % room_id, content)

test/admin_api_test.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import responses
2+
import json
3+
from matrix_client.admin_api import MatrixHttpAdminApi
4+
5+
6+
class TestAdminApi:
7+
admin_api = MatrixHttpAdminApi("http://example.com")
8+
user_id = "@alice:matrix.org"
9+
room_id = "!gveUzqBzXPqmwvDaCZ:example.org"
10+
event_id = "$153119074937XoqNn::example.org"
11+
up_to_ts = 1531190749090
12+
purge_id = "dLVEjckmfggyQduS"
13+
14+
@responses.activate
15+
def test_purge_history_eventid(self):
16+
purge_history_url = \
17+
"http://example.com/_matrix/client/r0/" \
18+
"admin/purge_history/%s" % self.room_id
19+
responses.add(
20+
responses.POST,
21+
purge_history_url,
22+
body='{"purge_id": "%s"}' % self.purge_id
23+
)
24+
self.admin_api.purge_history(self.room_id, self.event_id)
25+
req = responses.calls[0].request
26+
assert req.url == purge_history_url
27+
assert req.method == 'POST'
28+
j = json.loads(req.body)
29+
assert j["delete_local_events"]
30+
assert j["purge_up_to_event_id"] == self.event_id
31+
32+
@responses.activate
33+
def test_purge_history_up_to_ts(self):
34+
purge_history_url = \
35+
"http://example.com/_matrix/client/r0/" \
36+
"admin/purge_history/%s" % self.room_id
37+
responses.add(
38+
responses.POST,
39+
purge_history_url,
40+
body='{"purge_id": "%s"}' % self.purge_id
41+
)
42+
self.admin_api.purge_history(self.room_id, self.up_to_ts)
43+
req = responses.calls[0].request
44+
j = json.loads(req.body)
45+
assert j["delete_local_events"]
46+
assert j["purge_up_to_ts"] == self.up_to_ts
47+
48+
@responses.activate
49+
def test_purge_history_status(self):
50+
purge_history_status_url = \
51+
"http://example.com/_matrix/client/r0/" \
52+
"admin/purge_history_status/%s" % self.purge_id
53+
responses.add(
54+
responses.GET,
55+
purge_history_status_url,
56+
body='{"status": "complete"}'
57+
)
58+
self.admin_api.purge_history_status(self.purge_id)
59+
req = responses.calls[0].request
60+
assert req.url == purge_history_status_url
61+
62+
@responses.activate
63+
def test_media_in_room(self):
64+
media_url = \
65+
"http://example.com/_matrix/client/r0/" \
66+
"admin/room/%s/media" % self.room_id
67+
responses.add(
68+
responses.GET,
69+
media_url,
70+
body='{"local": ["mxc://example.com/xwvutsrqponmlkjihgfedcba"],'
71+
' "remote": ["mxc://matrix.org/xwtttsrqponmlkjihgfedcba"]}'
72+
)
73+
resp = self.admin_api.media_in_room(self.room_id)
74+
req = responses.calls[0].request
75+
assert req.url == media_url
76+
assert req.method == 'GET'
77+
assert "local" in resp
78+
assert "remote" in resp
79+
80+
@responses.activate
81+
def test_whois(self):
82+
whois_url = \
83+
"http://example.com/_matrix/client/r0/" \
84+
"admin/whois/%s" % self.user_id
85+
responses.add(
86+
responses.GET,
87+
whois_url,
88+
body='{"user_id": "%s", "devices": {}}' % self.user_id
89+
)
90+
self.admin_api.whois(self.user_id)
91+
req = responses.calls[0].request
92+
assert req.url == whois_url
93+
assert req.method == 'GET'
94+
95+
@responses.activate
96+
def test_deactivate_no_erase(self):
97+
erase_url = \
98+
"http://example.com/_matrix/client/r0/" \
99+
"admin/deactivate/%s" % self.user_id
100+
responses.add(responses.POST, erase_url, body='{}')
101+
self.admin_api.deactivate(self.user_id)
102+
req = responses.calls[0].request
103+
assert req.url == erase_url
104+
assert req.method == 'POST'
105+
106+
@responses.activate
107+
def test_deactivate(self):
108+
erase_url = \
109+
"http://example.com/_matrix/client/r0/" \
110+
"admin/deactivate/%s" % self.user_id
111+
responses.add(responses.POST, erase_url, body='{}')
112+
self.admin_api.deactivate(self.user_id, erase=True)
113+
req = responses.calls[0].request
114+
assert req.url == erase_url
115+
assert req.method == 'POST'
116+
j = json.loads(req.body)
117+
assert j["erase"]
118+
119+
@responses.activate
120+
def test_reset_password(self):
121+
reset_url = \
122+
"http://example.com/_matrix/client/r0/" \
123+
"admin/reset_password/%s" % self.user_id
124+
responses.add(responses.POST, reset_url, body='{}')
125+
self.admin_api.reset_password(self.user_id, 'secret')
126+
req = responses.calls[0].request
127+
assert req.url == reset_url
128+
assert req.method == 'POST'
129+
j = json.loads(req.body)
130+
assert j["new_password"] == 'secret'
131+
132+
@responses.activate
133+
def test_quarantine_media(self):
134+
quarantine_media_url = \
135+
"http://example.com/_matrix/client/r0/" \
136+
"admin/quarantine_media/%s" % self.room_id
137+
responses.add(
138+
responses.POST,
139+
quarantine_media_url,
140+
body='{"num_quarantined": 1}'
141+
)
142+
self.admin_api.quarantine_media(self.room_id)
143+
req = responses.calls[0].request
144+
assert req.url == quarantine_media_url
145+
assert req.method == 'POST'
146+
147+
@responses.activate
148+
def test_shutdown_room(self):
149+
shutdown_room_url = \
150+
"http://example.com/_matrix/client/r0/" \
151+
"admin/shutdown_room/%s" % self.room_id
152+
responses.add(
153+
responses.POST,
154+
shutdown_room_url,
155+
body='{"kicked_users": 2, '
156+
'"local_aliases": [], '
157+
'"new_room_id": "!hepuyalbwtkjapqdhq:example.org"}'
158+
)
159+
self.admin_api.shutdown_room(
160+
self.room_id,
161+
self.user_id,
162+
room_name="New room",
163+
message="Old room closed by admin"
164+
)
165+
req = responses.calls[0].request
166+
assert req.url == shutdown_room_url
167+
assert req.method == 'POST'
168+
j = json.loads(req.body)
169+
assert j["new_room_user_id"] == self.user_id

0 commit comments

Comments
 (0)