Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

496 righe
17 KiB

  1. # Copyright 2018-2021 The Matrix.org Foundation C.I.C.
  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. import urllib.parse
  15. from parameterized import parameterized
  16. from twisted.test.proto_helpers import MemoryReactor
  17. import synapse.rest.admin
  18. from synapse.http.server import JsonResource
  19. from synapse.rest.admin import VersionServlet
  20. from synapse.rest.client import login, room
  21. from synapse.server import HomeServer
  22. from synapse.util import Clock
  23. from tests import unittest
  24. from tests.server import FakeSite, make_request
  25. from tests.test_utils import SMALL_PNG
  26. class VersionTestCase(unittest.HomeserverTestCase):
  27. url = "/_synapse/admin/v1/server_version"
  28. def create_test_resource(self) -> JsonResource:
  29. resource = JsonResource(self.hs)
  30. VersionServlet(self.hs).register(resource)
  31. return resource
  32. def test_version_string(self) -> None:
  33. channel = self.make_request("GET", self.url, shorthand=False)
  34. self.assertEqual(200, channel.code, msg=channel.json_body)
  35. self.assertEqual({"server_version"}, set(channel.json_body.keys()))
  36. class QuarantineMediaTestCase(unittest.HomeserverTestCase):
  37. """Test /quarantine_media admin API."""
  38. servlets = [
  39. synapse.rest.admin.register_servlets,
  40. synapse.rest.admin.register_servlets_for_media_repo,
  41. login.register_servlets,
  42. room.register_servlets,
  43. ]
  44. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  45. # Allow for uploading and downloading to/from the media repo
  46. self.media_repo = hs.get_media_repository_resource()
  47. self.download_resource = self.media_repo.children[b"download"]
  48. self.upload_resource = self.media_repo.children[b"upload"]
  49. def _ensure_quarantined(
  50. self, admin_user_tok: str, server_and_media_id: str
  51. ) -> None:
  52. """Ensure a piece of media is quarantined when trying to access it."""
  53. channel = make_request(
  54. self.reactor,
  55. FakeSite(self.download_resource, self.reactor),
  56. "GET",
  57. server_and_media_id,
  58. shorthand=False,
  59. access_token=admin_user_tok,
  60. )
  61. # Should be quarantined
  62. self.assertEqual(
  63. 404,
  64. channel.code,
  65. msg=(
  66. "Expected to receive a 404 on accessing quarantined media: %s"
  67. % server_and_media_id
  68. ),
  69. )
  70. @parameterized.expand(
  71. [
  72. # Attempt quarantine media APIs as non-admin
  73. "/_synapse/admin/v1/media/quarantine/example.org/abcde12345",
  74. # And the roomID/userID endpoint
  75. "/_synapse/admin/v1/room/!room%3Aexample.com/media/quarantine",
  76. ]
  77. )
  78. def test_quarantine_media_requires_admin(self, url: str) -> None:
  79. self.register_user("nonadmin", "pass", admin=False)
  80. non_admin_user_tok = self.login("nonadmin", "pass")
  81. channel = self.make_request(
  82. "POST",
  83. url.encode("ascii"),
  84. access_token=non_admin_user_tok,
  85. )
  86. # Expect a forbidden error
  87. self.assertEqual(
  88. 403,
  89. channel.code,
  90. msg="Expected forbidden on quarantining media as a non-admin",
  91. )
  92. def test_quarantine_media_by_id(self) -> None:
  93. self.register_user("id_admin", "pass", admin=True)
  94. admin_user_tok = self.login("id_admin", "pass")
  95. self.register_user("id_nonadmin", "pass", admin=False)
  96. non_admin_user_tok = self.login("id_nonadmin", "pass")
  97. # Upload some media into the room
  98. response = self.helper.upload_media(
  99. self.upload_resource, SMALL_PNG, tok=admin_user_tok
  100. )
  101. # Extract media ID from the response
  102. server_name_and_media_id = response["content_uri"][6:] # Cut off 'mxc://'
  103. server_name, media_id = server_name_and_media_id.split("/")
  104. # Attempt to access the media
  105. channel = make_request(
  106. self.reactor,
  107. FakeSite(self.download_resource, self.reactor),
  108. "GET",
  109. server_name_and_media_id,
  110. shorthand=False,
  111. access_token=non_admin_user_tok,
  112. )
  113. # Should be successful
  114. self.assertEqual(200, channel.code)
  115. # Quarantine the media
  116. url = "/_synapse/admin/v1/media/quarantine/%s/%s" % (
  117. urllib.parse.quote(server_name),
  118. urllib.parse.quote(media_id),
  119. )
  120. channel = self.make_request(
  121. "POST",
  122. url,
  123. access_token=admin_user_tok,
  124. )
  125. self.pump(1.0)
  126. self.assertEqual(200, channel.code, msg=channel.json_body)
  127. # Attempt to access the media
  128. self._ensure_quarantined(admin_user_tok, server_name_and_media_id)
  129. @parameterized.expand(
  130. [
  131. # regular API path
  132. "/_synapse/admin/v1/room/%s/media/quarantine",
  133. # deprecated API path
  134. "/_synapse/admin/v1/quarantine_media/%s",
  135. ]
  136. )
  137. def test_quarantine_all_media_in_room(self, url: str) -> None:
  138. self.register_user("room_admin", "pass", admin=True)
  139. admin_user_tok = self.login("room_admin", "pass")
  140. non_admin_user = self.register_user("room_nonadmin", "pass", admin=False)
  141. non_admin_user_tok = self.login("room_nonadmin", "pass")
  142. room_id = self.helper.create_room_as(non_admin_user, tok=admin_user_tok)
  143. self.helper.join(room_id, non_admin_user, tok=non_admin_user_tok)
  144. # Upload some media
  145. response_1 = self.helper.upload_media(
  146. self.upload_resource, SMALL_PNG, tok=non_admin_user_tok
  147. )
  148. response_2 = self.helper.upload_media(
  149. self.upload_resource, SMALL_PNG, tok=non_admin_user_tok
  150. )
  151. # Extract mxcs
  152. mxc_1 = response_1["content_uri"]
  153. mxc_2 = response_2["content_uri"]
  154. # Send it into the room
  155. self.helper.send_event(
  156. room_id,
  157. "m.room.message",
  158. content={"body": "image-1", "msgtype": "m.image", "url": mxc_1},
  159. txn_id="111",
  160. tok=non_admin_user_tok,
  161. )
  162. self.helper.send_event(
  163. room_id,
  164. "m.room.message",
  165. content={"body": "image-2", "msgtype": "m.image", "url": mxc_2},
  166. txn_id="222",
  167. tok=non_admin_user_tok,
  168. )
  169. channel = self.make_request(
  170. "POST",
  171. url % urllib.parse.quote(room_id),
  172. access_token=admin_user_tok,
  173. )
  174. self.pump(1.0)
  175. self.assertEqual(200, channel.code, msg=channel.json_body)
  176. self.assertEqual(
  177. channel.json_body, {"num_quarantined": 2}, "Expected 2 quarantined items"
  178. )
  179. # Convert mxc URLs to server/media_id strings
  180. server_and_media_id_1 = mxc_1[6:]
  181. server_and_media_id_2 = mxc_2[6:]
  182. # Test that we cannot download any of the media anymore
  183. self._ensure_quarantined(admin_user_tok, server_and_media_id_1)
  184. self._ensure_quarantined(admin_user_tok, server_and_media_id_2)
  185. def test_quarantine_all_media_by_user(self) -> None:
  186. self.register_user("user_admin", "pass", admin=True)
  187. admin_user_tok = self.login("user_admin", "pass")
  188. non_admin_user = self.register_user("user_nonadmin", "pass", admin=False)
  189. non_admin_user_tok = self.login("user_nonadmin", "pass")
  190. # Upload some media
  191. response_1 = self.helper.upload_media(
  192. self.upload_resource, SMALL_PNG, tok=non_admin_user_tok
  193. )
  194. response_2 = self.helper.upload_media(
  195. self.upload_resource, SMALL_PNG, tok=non_admin_user_tok
  196. )
  197. # Extract media IDs
  198. server_and_media_id_1 = response_1["content_uri"][6:]
  199. server_and_media_id_2 = response_2["content_uri"][6:]
  200. # Quarantine all media by this user
  201. url = "/_synapse/admin/v1/user/%s/media/quarantine" % urllib.parse.quote(
  202. non_admin_user
  203. )
  204. channel = self.make_request(
  205. "POST",
  206. url.encode("ascii"),
  207. access_token=admin_user_tok,
  208. )
  209. self.pump(1.0)
  210. self.assertEqual(200, channel.code, msg=channel.json_body)
  211. self.assertEqual(
  212. channel.json_body, {"num_quarantined": 2}, "Expected 2 quarantined items"
  213. )
  214. # Attempt to access each piece of media
  215. self._ensure_quarantined(admin_user_tok, server_and_media_id_1)
  216. self._ensure_quarantined(admin_user_tok, server_and_media_id_2)
  217. def test_cannot_quarantine_safe_media(self) -> None:
  218. self.register_user("user_admin", "pass", admin=True)
  219. admin_user_tok = self.login("user_admin", "pass")
  220. non_admin_user = self.register_user("user_nonadmin", "pass", admin=False)
  221. non_admin_user_tok = self.login("user_nonadmin", "pass")
  222. # Upload some media
  223. response_1 = self.helper.upload_media(
  224. self.upload_resource, SMALL_PNG, tok=non_admin_user_tok
  225. )
  226. response_2 = self.helper.upload_media(
  227. self.upload_resource, SMALL_PNG, tok=non_admin_user_tok
  228. )
  229. # Extract media IDs
  230. server_and_media_id_1 = response_1["content_uri"][6:]
  231. server_and_media_id_2 = response_2["content_uri"][6:]
  232. # Mark the second item as safe from quarantine.
  233. _, media_id_2 = server_and_media_id_2.split("/")
  234. # Quarantine the media
  235. url = "/_synapse/admin/v1/media/protect/%s" % (urllib.parse.quote(media_id_2),)
  236. channel = self.make_request("POST", url, access_token=admin_user_tok)
  237. self.pump(1.0)
  238. self.assertEqual(200, channel.code, msg=channel.json_body)
  239. # Quarantine all media by this user
  240. url = "/_synapse/admin/v1/user/%s/media/quarantine" % urllib.parse.quote(
  241. non_admin_user
  242. )
  243. channel = self.make_request(
  244. "POST",
  245. url.encode("ascii"),
  246. access_token=admin_user_tok,
  247. )
  248. self.pump(1.0)
  249. self.assertEqual(200, channel.code, msg=channel.json_body)
  250. self.assertEqual(
  251. channel.json_body, {"num_quarantined": 1}, "Expected 1 quarantined item"
  252. )
  253. # Attempt to access each piece of media, the first should fail, the
  254. # second should succeed.
  255. self._ensure_quarantined(admin_user_tok, server_and_media_id_1)
  256. # Attempt to access each piece of media
  257. channel = make_request(
  258. self.reactor,
  259. FakeSite(self.download_resource, self.reactor),
  260. "GET",
  261. server_and_media_id_2,
  262. shorthand=False,
  263. access_token=non_admin_user_tok,
  264. )
  265. # Shouldn't be quarantined
  266. self.assertEqual(
  267. 200,
  268. channel.code,
  269. msg=(
  270. "Expected to receive a 200 on accessing not-quarantined media: %s"
  271. % server_and_media_id_2
  272. ),
  273. )
  274. class PurgeHistoryTestCase(unittest.HomeserverTestCase):
  275. servlets = [
  276. synapse.rest.admin.register_servlets,
  277. login.register_servlets,
  278. room.register_servlets,
  279. ]
  280. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  281. self.admin_user = self.register_user("admin", "pass", admin=True)
  282. self.admin_user_tok = self.login("admin", "pass")
  283. self.other_user = self.register_user("user", "pass")
  284. self.other_user_tok = self.login("user", "pass")
  285. self.room_id = self.helper.create_room_as(
  286. self.other_user, tok=self.other_user_tok
  287. )
  288. self.url = f"/_synapse/admin/v1/purge_history/{self.room_id}"
  289. self.url_status = "/_synapse/admin/v1/purge_history_status/"
  290. def test_purge_history(self) -> None:
  291. """
  292. Simple test of purge history API.
  293. Test only that is is possible to call, get status 200 and purge_id.
  294. """
  295. channel = self.make_request(
  296. "POST",
  297. self.url,
  298. content={"delete_local_events": True, "purge_up_to_ts": 0},
  299. access_token=self.admin_user_tok,
  300. )
  301. self.assertEqual(200, channel.code, msg=channel.json_body)
  302. self.assertIn("purge_id", channel.json_body)
  303. purge_id = channel.json_body["purge_id"]
  304. # get status
  305. channel = self.make_request(
  306. "GET",
  307. self.url_status + purge_id,
  308. access_token=self.admin_user_tok,
  309. )
  310. self.assertEqual(200, channel.code, msg=channel.json_body)
  311. self.assertEqual("complete", channel.json_body["status"])
  312. class ExperimentalFeaturesTestCase(unittest.HomeserverTestCase):
  313. servlets = [
  314. synapse.rest.admin.register_servlets,
  315. login.register_servlets,
  316. ]
  317. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  318. self.admin_user = self.register_user("admin", "pass", admin=True)
  319. self.admin_user_tok = self.login("admin", "pass")
  320. self.other_user = self.register_user("user", "pass")
  321. self.other_user_tok = self.login("user", "pass")
  322. self.url = "/_synapse/admin/v1/experimental_features"
  323. def test_enable_and_disable(self) -> None:
  324. """
  325. Test basic functionality of ExperimentalFeatures endpoint
  326. """
  327. # test enabling features works
  328. url = f"{self.url}/{self.other_user}"
  329. channel = self.make_request(
  330. "PUT",
  331. url,
  332. content={
  333. "features": {"msc3026": True, "msc3881": True},
  334. },
  335. access_token=self.admin_user_tok,
  336. )
  337. self.assertEqual(channel.code, 200)
  338. # list which features are enabled and ensure the ones we enabled are listed
  339. self.assertEqual(channel.code, 200)
  340. url = f"{self.url}/{self.other_user}"
  341. channel = self.make_request(
  342. "GET",
  343. url,
  344. access_token=self.admin_user_tok,
  345. )
  346. self.assertEqual(channel.code, 200)
  347. self.assertEqual(
  348. True,
  349. channel.json_body["features"]["msc3026"],
  350. )
  351. self.assertEqual(
  352. True,
  353. channel.json_body["features"]["msc3881"],
  354. )
  355. # test disabling a feature works
  356. url = f"{self.url}/{self.other_user}"
  357. channel = self.make_request(
  358. "PUT",
  359. url,
  360. content={"features": {"msc3026": False}},
  361. access_token=self.admin_user_tok,
  362. )
  363. self.assertEqual(channel.code, 200)
  364. # list the features enabled/disabled and ensure they are still are correct
  365. self.assertEqual(channel.code, 200)
  366. url = f"{self.url}/{self.other_user}"
  367. channel = self.make_request(
  368. "GET",
  369. url,
  370. access_token=self.admin_user_tok,
  371. )
  372. self.assertEqual(channel.code, 200)
  373. self.assertEqual(
  374. False,
  375. channel.json_body["features"]["msc3026"],
  376. )
  377. self.assertEqual(
  378. True,
  379. channel.json_body["features"]["msc3881"],
  380. )
  381. self.assertEqual(
  382. False,
  383. channel.json_body["features"]["msc3967"],
  384. )
  385. # test nothing blows up if you try to disable a feature that isn't already enabled
  386. url = f"{self.url}/{self.other_user}"
  387. channel = self.make_request(
  388. "PUT",
  389. url,
  390. content={"features": {"msc3026": False}},
  391. access_token=self.admin_user_tok,
  392. )
  393. self.assertEqual(channel.code, 200)
  394. # test trying to enable a feature without an admin access token is denied
  395. url = f"{self.url}/f{self.other_user}"
  396. channel = self.make_request(
  397. "PUT",
  398. url,
  399. content={"features": {"msc3881": True}},
  400. access_token=self.other_user_tok,
  401. )
  402. self.assertEqual(channel.code, 403)
  403. self.assertEqual(
  404. channel.json_body,
  405. {"errcode": "M_FORBIDDEN", "error": "You are not a server admin"},
  406. )
  407. # test trying to enable a bogus msc is denied
  408. url = f"{self.url}/{self.other_user}"
  409. channel = self.make_request(
  410. "PUT",
  411. url,
  412. content={"features": {"msc6666": True}},
  413. access_token=self.admin_user_tok,
  414. )
  415. self.assertEqual(channel.code, 400)
  416. self.assertEqual(
  417. channel.json_body,
  418. {
  419. "errcode": "M_UNKNOWN",
  420. "error": "'msc6666' is not recognised as a valid experimental feature.",
  421. },
  422. )