test_event_reports.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. # Copyright 2020 Dirk Klimpel
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from typing import List
  15. from twisted.test.proto_helpers import MemoryReactor
  16. import synapse.rest.admin
  17. from synapse.api.errors import Codes
  18. from synapse.rest.client import login, report_event, room
  19. from synapse.server import HomeServer
  20. from synapse.types import JsonDict
  21. from synapse.util import Clock
  22. from tests import unittest
  23. class EventReportsTestCase(unittest.HomeserverTestCase):
  24. servlets = [
  25. synapse.rest.admin.register_servlets,
  26. login.register_servlets,
  27. room.register_servlets,
  28. report_event.register_servlets,
  29. ]
  30. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  31. self.admin_user = self.register_user("admin", "pass", admin=True)
  32. self.admin_user_tok = self.login("admin", "pass")
  33. self.other_user = self.register_user("user", "pass")
  34. self.other_user_tok = self.login("user", "pass")
  35. self.room_id1 = self.helper.create_room_as(
  36. self.other_user, tok=self.other_user_tok, is_public=True
  37. )
  38. self.helper.join(self.room_id1, user=self.admin_user, tok=self.admin_user_tok)
  39. self.room_id2 = self.helper.create_room_as(
  40. self.other_user, tok=self.other_user_tok, is_public=True
  41. )
  42. self.helper.join(self.room_id2, user=self.admin_user, tok=self.admin_user_tok)
  43. # Two rooms and two users. Every user sends and reports every room event
  44. for _ in range(5):
  45. self._create_event_and_report(
  46. room_id=self.room_id1,
  47. user_tok=self.other_user_tok,
  48. )
  49. for _ in range(5):
  50. self._create_event_and_report(
  51. room_id=self.room_id2,
  52. user_tok=self.other_user_tok,
  53. )
  54. for _ in range(5):
  55. self._create_event_and_report(
  56. room_id=self.room_id1,
  57. user_tok=self.admin_user_tok,
  58. )
  59. for _ in range(5):
  60. self._create_event_and_report_without_parameters(
  61. room_id=self.room_id2,
  62. user_tok=self.admin_user_tok,
  63. )
  64. self.url = "/_synapse/admin/v1/event_reports"
  65. def test_no_auth(self) -> None:
  66. """
  67. Try to get an event report without authentication.
  68. """
  69. channel = self.make_request("GET", self.url, b"{}")
  70. self.assertEqual(401, channel.code, msg=channel.json_body)
  71. self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"])
  72. def test_requester_is_no_admin(self) -> None:
  73. """
  74. If the user is not a server admin, an error 403 is returned.
  75. """
  76. channel = self.make_request(
  77. "GET",
  78. self.url,
  79. access_token=self.other_user_tok,
  80. )
  81. self.assertEqual(403, channel.code, msg=channel.json_body)
  82. self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"])
  83. def test_default_success(self) -> None:
  84. """
  85. Testing list of reported events
  86. """
  87. channel = self.make_request(
  88. "GET",
  89. self.url,
  90. access_token=self.admin_user_tok,
  91. )
  92. self.assertEqual(200, channel.code, msg=channel.json_body)
  93. self.assertEqual(channel.json_body["total"], 20)
  94. self.assertEqual(len(channel.json_body["event_reports"]), 20)
  95. self.assertNotIn("next_token", channel.json_body)
  96. self._check_fields(channel.json_body["event_reports"])
  97. def test_limit(self) -> None:
  98. """
  99. Testing list of reported events with limit
  100. """
  101. channel = self.make_request(
  102. "GET",
  103. self.url + "?limit=5",
  104. access_token=self.admin_user_tok,
  105. )
  106. self.assertEqual(200, channel.code, msg=channel.json_body)
  107. self.assertEqual(channel.json_body["total"], 20)
  108. self.assertEqual(len(channel.json_body["event_reports"]), 5)
  109. self.assertEqual(channel.json_body["next_token"], 5)
  110. self._check_fields(channel.json_body["event_reports"])
  111. def test_from(self) -> None:
  112. """
  113. Testing list of reported events with a defined starting point (from)
  114. """
  115. channel = self.make_request(
  116. "GET",
  117. self.url + "?from=5",
  118. access_token=self.admin_user_tok,
  119. )
  120. self.assertEqual(200, channel.code, msg=channel.json_body)
  121. self.assertEqual(channel.json_body["total"], 20)
  122. self.assertEqual(len(channel.json_body["event_reports"]), 15)
  123. self.assertNotIn("next_token", channel.json_body)
  124. self._check_fields(channel.json_body["event_reports"])
  125. def test_limit_and_from(self) -> None:
  126. """
  127. Testing list of reported events with a defined starting point and limit
  128. """
  129. channel = self.make_request(
  130. "GET",
  131. self.url + "?from=5&limit=10",
  132. access_token=self.admin_user_tok,
  133. )
  134. self.assertEqual(200, channel.code, msg=channel.json_body)
  135. self.assertEqual(channel.json_body["total"], 20)
  136. self.assertEqual(channel.json_body["next_token"], 15)
  137. self.assertEqual(len(channel.json_body["event_reports"]), 10)
  138. self._check_fields(channel.json_body["event_reports"])
  139. def test_filter_room(self) -> None:
  140. """
  141. Testing list of reported events with a filter of room
  142. """
  143. channel = self.make_request(
  144. "GET",
  145. self.url + "?room_id=%s" % self.room_id1,
  146. access_token=self.admin_user_tok,
  147. )
  148. self.assertEqual(200, channel.code, msg=channel.json_body)
  149. self.assertEqual(channel.json_body["total"], 10)
  150. self.assertEqual(len(channel.json_body["event_reports"]), 10)
  151. self.assertNotIn("next_token", channel.json_body)
  152. self._check_fields(channel.json_body["event_reports"])
  153. for report in channel.json_body["event_reports"]:
  154. self.assertEqual(report["room_id"], self.room_id1)
  155. def test_filter_user(self) -> None:
  156. """
  157. Testing list of reported events with a filter of user
  158. """
  159. channel = self.make_request(
  160. "GET",
  161. self.url + "?user_id=%s" % self.other_user,
  162. access_token=self.admin_user_tok,
  163. )
  164. self.assertEqual(200, channel.code, msg=channel.json_body)
  165. self.assertEqual(channel.json_body["total"], 10)
  166. self.assertEqual(len(channel.json_body["event_reports"]), 10)
  167. self.assertNotIn("next_token", channel.json_body)
  168. self._check_fields(channel.json_body["event_reports"])
  169. for report in channel.json_body["event_reports"]:
  170. self.assertEqual(report["user_id"], self.other_user)
  171. def test_filter_user_and_room(self) -> None:
  172. """
  173. Testing list of reported events with a filter of user and room
  174. """
  175. channel = self.make_request(
  176. "GET",
  177. self.url + "?user_id=%s&room_id=%s" % (self.other_user, self.room_id1),
  178. access_token=self.admin_user_tok,
  179. )
  180. self.assertEqual(200, channel.code, msg=channel.json_body)
  181. self.assertEqual(channel.json_body["total"], 5)
  182. self.assertEqual(len(channel.json_body["event_reports"]), 5)
  183. self.assertNotIn("next_token", channel.json_body)
  184. self._check_fields(channel.json_body["event_reports"])
  185. for report in channel.json_body["event_reports"]:
  186. self.assertEqual(report["user_id"], self.other_user)
  187. self.assertEqual(report["room_id"], self.room_id1)
  188. def test_valid_search_order(self) -> None:
  189. """
  190. Testing search order. Order by timestamps.
  191. """
  192. # fetch the most recent first, largest timestamp
  193. channel = self.make_request(
  194. "GET",
  195. self.url + "?dir=b",
  196. access_token=self.admin_user_tok,
  197. )
  198. self.assertEqual(200, channel.code, msg=channel.json_body)
  199. self.assertEqual(channel.json_body["total"], 20)
  200. self.assertEqual(len(channel.json_body["event_reports"]), 20)
  201. report = 1
  202. while report < len(channel.json_body["event_reports"]):
  203. self.assertGreaterEqual(
  204. channel.json_body["event_reports"][report - 1]["received_ts"],
  205. channel.json_body["event_reports"][report]["received_ts"],
  206. )
  207. report += 1
  208. # fetch the oldest first, smallest timestamp
  209. channel = self.make_request(
  210. "GET",
  211. self.url + "?dir=f",
  212. access_token=self.admin_user_tok,
  213. )
  214. self.assertEqual(200, channel.code, msg=channel.json_body)
  215. self.assertEqual(channel.json_body["total"], 20)
  216. self.assertEqual(len(channel.json_body["event_reports"]), 20)
  217. report = 1
  218. while report < len(channel.json_body["event_reports"]):
  219. self.assertLessEqual(
  220. channel.json_body["event_reports"][report - 1]["received_ts"],
  221. channel.json_body["event_reports"][report]["received_ts"],
  222. )
  223. report += 1
  224. def test_invalid_search_order(self) -> None:
  225. """
  226. Testing that a invalid search order returns a 400
  227. """
  228. channel = self.make_request(
  229. "GET",
  230. self.url + "?dir=bar",
  231. access_token=self.admin_user_tok,
  232. )
  233. self.assertEqual(400, channel.code, msg=channel.json_body)
  234. self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
  235. self.assertEqual("Unknown direction: bar", channel.json_body["error"])
  236. def test_limit_is_negative(self) -> None:
  237. """
  238. Testing that a negative limit parameter returns a 400
  239. """
  240. channel = self.make_request(
  241. "GET",
  242. self.url + "?limit=-5",
  243. access_token=self.admin_user_tok,
  244. )
  245. self.assertEqual(400, channel.code, msg=channel.json_body)
  246. self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
  247. def test_from_is_negative(self) -> None:
  248. """
  249. Testing that a negative from parameter returns a 400
  250. """
  251. channel = self.make_request(
  252. "GET",
  253. self.url + "?from=-5",
  254. access_token=self.admin_user_tok,
  255. )
  256. self.assertEqual(400, channel.code, msg=channel.json_body)
  257. self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
  258. def test_next_token(self) -> None:
  259. """
  260. Testing that `next_token` appears at the right place
  261. """
  262. # `next_token` does not appear
  263. # Number of results is the number of entries
  264. channel = self.make_request(
  265. "GET",
  266. self.url + "?limit=20",
  267. access_token=self.admin_user_tok,
  268. )
  269. self.assertEqual(200, channel.code, msg=channel.json_body)
  270. self.assertEqual(channel.json_body["total"], 20)
  271. self.assertEqual(len(channel.json_body["event_reports"]), 20)
  272. self.assertNotIn("next_token", channel.json_body)
  273. # `next_token` does not appear
  274. # Number of max results is larger than the number of entries
  275. channel = self.make_request(
  276. "GET",
  277. self.url + "?limit=21",
  278. access_token=self.admin_user_tok,
  279. )
  280. self.assertEqual(200, channel.code, msg=channel.json_body)
  281. self.assertEqual(channel.json_body["total"], 20)
  282. self.assertEqual(len(channel.json_body["event_reports"]), 20)
  283. self.assertNotIn("next_token", channel.json_body)
  284. # `next_token` does appear
  285. # Number of max results is smaller than the number of entries
  286. channel = self.make_request(
  287. "GET",
  288. self.url + "?limit=19",
  289. access_token=self.admin_user_tok,
  290. )
  291. self.assertEqual(200, channel.code, msg=channel.json_body)
  292. self.assertEqual(channel.json_body["total"], 20)
  293. self.assertEqual(len(channel.json_body["event_reports"]), 19)
  294. self.assertEqual(channel.json_body["next_token"], 19)
  295. # Check
  296. # Set `from` to value of `next_token` for request remaining entries
  297. # `next_token` does not appear
  298. channel = self.make_request(
  299. "GET",
  300. self.url + "?from=19",
  301. access_token=self.admin_user_tok,
  302. )
  303. self.assertEqual(200, channel.code, msg=channel.json_body)
  304. self.assertEqual(channel.json_body["total"], 20)
  305. self.assertEqual(len(channel.json_body["event_reports"]), 1)
  306. self.assertNotIn("next_token", channel.json_body)
  307. def _create_event_and_report(self, room_id: str, user_tok: str) -> None:
  308. """Create and report events"""
  309. resp = self.helper.send(room_id, tok=user_tok)
  310. event_id = resp["event_id"]
  311. channel = self.make_request(
  312. "POST",
  313. "rooms/%s/report/%s" % (room_id, event_id),
  314. {"score": -100, "reason": "this makes me sad"},
  315. access_token=user_tok,
  316. )
  317. self.assertEqual(200, channel.code, msg=channel.json_body)
  318. def _create_event_and_report_without_parameters(
  319. self, room_id: str, user_tok: str
  320. ) -> None:
  321. """Create and report an event, but omit reason and score"""
  322. resp = self.helper.send(room_id, tok=user_tok)
  323. event_id = resp["event_id"]
  324. channel = self.make_request(
  325. "POST",
  326. "rooms/%s/report/%s" % (room_id, event_id),
  327. {},
  328. access_token=user_tok,
  329. )
  330. self.assertEqual(200, channel.code, msg=channel.json_body)
  331. def _check_fields(self, content: List[JsonDict]) -> None:
  332. """Checks that all attributes are present in an event report"""
  333. for c in content:
  334. self.assertIn("id", c)
  335. self.assertIn("received_ts", c)
  336. self.assertIn("room_id", c)
  337. self.assertIn("event_id", c)
  338. self.assertIn("user_id", c)
  339. self.assertIn("sender", c)
  340. self.assertIn("canonical_alias", c)
  341. self.assertIn("name", c)
  342. self.assertIn("score", c)
  343. self.assertIn("reason", c)
  344. def test_count_correct_despite_table_deletions(self) -> None:
  345. """
  346. Tests that the count matches the number of rows, even if rows in joined tables
  347. are missing.
  348. """
  349. # Delete rows from room_stats_state for one of our rooms.
  350. self.get_success(
  351. self.hs.get_datastores().main.db_pool.simple_delete(
  352. "room_stats_state", {"room_id": self.room_id1}, desc="_"
  353. )
  354. )
  355. channel = self.make_request(
  356. "GET",
  357. self.url,
  358. access_token=self.admin_user_tok,
  359. )
  360. self.assertEqual(200, channel.code, msg=channel.json_body)
  361. # The 'total' field is 10 because only 10 reports will actually
  362. # be retrievable since we deleted the rows in the room_stats_state
  363. # table.
  364. self.assertEqual(channel.json_body["total"], 10)
  365. # This is consistent with the number of rows actually returned.
  366. self.assertEqual(len(channel.json_body["event_reports"]), 10)
  367. class EventReportDetailTestCase(unittest.HomeserverTestCase):
  368. servlets = [
  369. synapse.rest.admin.register_servlets,
  370. login.register_servlets,
  371. room.register_servlets,
  372. report_event.register_servlets,
  373. ]
  374. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  375. self.admin_user = self.register_user("admin", "pass", admin=True)
  376. self.admin_user_tok = self.login("admin", "pass")
  377. self.other_user = self.register_user("user", "pass")
  378. self.other_user_tok = self.login("user", "pass")
  379. self.room_id1 = self.helper.create_room_as(
  380. self.other_user, tok=self.other_user_tok, is_public=True
  381. )
  382. self.helper.join(self.room_id1, user=self.admin_user, tok=self.admin_user_tok)
  383. self._create_event_and_report(
  384. room_id=self.room_id1,
  385. user_tok=self.other_user_tok,
  386. )
  387. # first created event report gets `id`=2
  388. self.url = "/_synapse/admin/v1/event_reports/2"
  389. def test_no_auth(self) -> None:
  390. """
  391. Try to get event report without authentication.
  392. """
  393. channel = self.make_request("GET", self.url, b"{}")
  394. self.assertEqual(401, channel.code, msg=channel.json_body)
  395. self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"])
  396. def test_requester_is_no_admin(self) -> None:
  397. """
  398. If the user is not a server admin, an error 403 is returned.
  399. """
  400. channel = self.make_request(
  401. "GET",
  402. self.url,
  403. access_token=self.other_user_tok,
  404. )
  405. self.assertEqual(403, channel.code, msg=channel.json_body)
  406. self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"])
  407. def test_default_success(self) -> None:
  408. """
  409. Testing get a reported event
  410. """
  411. channel = self.make_request(
  412. "GET",
  413. self.url,
  414. access_token=self.admin_user_tok,
  415. )
  416. self.assertEqual(200, channel.code, msg=channel.json_body)
  417. self._check_fields(channel.json_body)
  418. def test_invalid_report_id(self) -> None:
  419. """
  420. Testing that an invalid `report_id` returns a 400.
  421. """
  422. # `report_id` is negative
  423. channel = self.make_request(
  424. "GET",
  425. "/_synapse/admin/v1/event_reports/-123",
  426. access_token=self.admin_user_tok,
  427. )
  428. self.assertEqual(400, channel.code, msg=channel.json_body)
  429. self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
  430. self.assertEqual(
  431. "The report_id parameter must be a string representing a positive integer.",
  432. channel.json_body["error"],
  433. )
  434. # `report_id` is a non-numerical string
  435. channel = self.make_request(
  436. "GET",
  437. "/_synapse/admin/v1/event_reports/abcdef",
  438. access_token=self.admin_user_tok,
  439. )
  440. self.assertEqual(400, channel.code, msg=channel.json_body)
  441. self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
  442. self.assertEqual(
  443. "The report_id parameter must be a string representing a positive integer.",
  444. channel.json_body["error"],
  445. )
  446. # `report_id` is undefined
  447. channel = self.make_request(
  448. "GET",
  449. "/_synapse/admin/v1/event_reports/",
  450. access_token=self.admin_user_tok,
  451. )
  452. self.assertEqual(400, channel.code, msg=channel.json_body)
  453. self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
  454. self.assertEqual(
  455. "The report_id parameter must be a string representing a positive integer.",
  456. channel.json_body["error"],
  457. )
  458. def test_report_id_not_found(self) -> None:
  459. """
  460. Testing that a not existing `report_id` returns a 404.
  461. """
  462. channel = self.make_request(
  463. "GET",
  464. "/_synapse/admin/v1/event_reports/123",
  465. access_token=self.admin_user_tok,
  466. )
  467. self.assertEqual(404, channel.code, msg=channel.json_body)
  468. self.assertEqual(Codes.NOT_FOUND, channel.json_body["errcode"])
  469. self.assertEqual("Event report not found", channel.json_body["error"])
  470. def _create_event_and_report(self, room_id: str, user_tok: str) -> None:
  471. """Create and report events"""
  472. resp = self.helper.send(room_id, tok=user_tok)
  473. event_id = resp["event_id"]
  474. channel = self.make_request(
  475. "POST",
  476. "rooms/%s/report/%s" % (room_id, event_id),
  477. {"score": -100, "reason": "this makes me sad"},
  478. access_token=user_tok,
  479. )
  480. self.assertEqual(200, channel.code, msg=channel.json_body)
  481. def _check_fields(self, content: JsonDict) -> None:
  482. """Checks that all attributes are present in a event report"""
  483. self.assertIn("id", content)
  484. self.assertIn("received_ts", content)
  485. self.assertIn("room_id", content)
  486. self.assertIn("event_id", content)
  487. self.assertIn("user_id", content)
  488. self.assertIn("sender", content)
  489. self.assertIn("canonical_alias", content)
  490. self.assertIn("name", content)
  491. self.assertIn("event_json", content)
  492. self.assertIn("score", content)
  493. self.assertIn("reason", content)
  494. self.assertIn("auth_events", content["event_json"])
  495. self.assertIn("type", content["event_json"])
  496. self.assertIn("room_id", content["event_json"])
  497. self.assertIn("sender", content["event_json"])
  498. self.assertIn("content", content["event_json"])