test_federation_server.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. # Copyright 2018 New Vector Ltd
  2. # Copyright 2019 Matrix.org Federation C.I.C
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import logging
  16. from http import HTTPStatus
  17. from parameterized import parameterized
  18. from twisted.test.proto_helpers import MemoryReactor
  19. from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
  20. from synapse.config.server import DEFAULT_ROOM_VERSION
  21. from synapse.events import EventBase, make_event_from_dict
  22. from synapse.rest import admin
  23. from synapse.rest.client import login, room
  24. from synapse.server import HomeServer
  25. from synapse.storage.controllers.state import server_acl_evaluator_from_event
  26. from synapse.types import JsonDict
  27. from synapse.util import Clock
  28. from tests import unittest
  29. from tests.unittest import override_config
  30. class FederationServerTests(unittest.FederatingHomeserverTestCase):
  31. servlets = [
  32. admin.register_servlets,
  33. room.register_servlets,
  34. login.register_servlets,
  35. ]
  36. @parameterized.expand([(b"",), (b"foo",), (b'{"limit": Infinity}',)])
  37. def test_bad_request(self, query_content: bytes) -> None:
  38. """
  39. Querying with bad data returns a reasonable error code.
  40. """
  41. u1 = self.register_user("u1", "pass")
  42. u1_token = self.login("u1", "pass")
  43. room_1 = self.helper.create_room_as(u1, tok=u1_token)
  44. self.inject_room_member(room_1, "@user:other.example.com", "join")
  45. "/get_missing_events/(?P<room_id>[^/]*)/?"
  46. channel = self.make_request(
  47. "POST",
  48. "/_matrix/federation/v1/get_missing_events/%s" % (room_1,),
  49. query_content,
  50. )
  51. self.assertEqual(HTTPStatus.BAD_REQUEST, channel.code, channel.result)
  52. self.assertEqual(channel.json_body["errcode"], "M_NOT_JSON")
  53. class ServerACLsTestCase(unittest.TestCase):
  54. def test_blocked_server(self) -> None:
  55. e = _create_acl_event({"allow": ["*"], "deny": ["evil.com"]})
  56. logging.info("ACL event: %s", e.content)
  57. server_acl_evalutor = server_acl_evaluator_from_event(e)
  58. self.assertFalse(server_acl_evalutor.server_matches_acl_event("evil.com"))
  59. self.assertFalse(server_acl_evalutor.server_matches_acl_event("EVIL.COM"))
  60. self.assertTrue(server_acl_evalutor.server_matches_acl_event("evil.com.au"))
  61. self.assertTrue(
  62. server_acl_evalutor.server_matches_acl_event("honestly.not.evil.com")
  63. )
  64. def test_block_ip_literals(self) -> None:
  65. e = _create_acl_event({"allow_ip_literals": False, "allow": ["*"]})
  66. logging.info("ACL event: %s", e.content)
  67. server_acl_evalutor = server_acl_evaluator_from_event(e)
  68. self.assertFalse(server_acl_evalutor.server_matches_acl_event("1.2.3.4"))
  69. self.assertTrue(server_acl_evalutor.server_matches_acl_event("1a.2.3.4"))
  70. self.assertFalse(server_acl_evalutor.server_matches_acl_event("[1:2::]"))
  71. self.assertTrue(server_acl_evalutor.server_matches_acl_event("1:2:3:4"))
  72. def test_wildcard_matching(self) -> None:
  73. e = _create_acl_event({"allow": ["good*.com"]})
  74. server_acl_evalutor = server_acl_evaluator_from_event(e)
  75. self.assertTrue(
  76. server_acl_evalutor.server_matches_acl_event("good.com"),
  77. "* matches 0 characters",
  78. )
  79. self.assertTrue(
  80. server_acl_evalutor.server_matches_acl_event("GOOD.COM"),
  81. "pattern is case-insensitive",
  82. )
  83. self.assertTrue(
  84. server_acl_evalutor.server_matches_acl_event("good.aa.com"),
  85. "* matches several characters, including '.'",
  86. )
  87. self.assertFalse(
  88. server_acl_evalutor.server_matches_acl_event("ishgood.com"),
  89. "pattern does not allow prefixes",
  90. )
  91. class StateQueryTests(unittest.FederatingHomeserverTestCase):
  92. servlets = [
  93. admin.register_servlets,
  94. room.register_servlets,
  95. login.register_servlets,
  96. ]
  97. def test_needs_to_be_in_room(self) -> None:
  98. """/v1/state/<room_id> requires the server to be in the room"""
  99. u1 = self.register_user("u1", "pass")
  100. u1_token = self.login("u1", "pass")
  101. room_1 = self.helper.create_room_as(u1, tok=u1_token)
  102. channel = self.make_signed_federation_request(
  103. "GET", "/_matrix/federation/v1/state/%s?event_id=xyz" % (room_1,)
  104. )
  105. self.assertEqual(HTTPStatus.FORBIDDEN, channel.code, channel.result)
  106. self.assertEqual(channel.json_body["errcode"], "M_FORBIDDEN")
  107. class SendJoinFederationTests(unittest.FederatingHomeserverTestCase):
  108. servlets = [
  109. admin.register_servlets,
  110. room.register_servlets,
  111. login.register_servlets,
  112. ]
  113. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  114. super().prepare(reactor, clock, hs)
  115. self._storage_controllers = hs.get_storage_controllers()
  116. # create the room
  117. creator_user_id = self.register_user("kermit", "test")
  118. tok = self.login("kermit", "test")
  119. self._room_id = self.helper.create_room_as(
  120. room_creator=creator_user_id, tok=tok
  121. )
  122. # a second member on the orgin HS
  123. second_member_user_id = self.register_user("fozzie", "bear")
  124. tok2 = self.login("fozzie", "bear")
  125. self.helper.join(self._room_id, second_member_user_id, tok=tok2)
  126. def _make_join(self, user_id: str) -> JsonDict:
  127. channel = self.make_signed_federation_request(
  128. "GET",
  129. f"/_matrix/federation/v1/make_join/{self._room_id}/{user_id}"
  130. f"?ver={DEFAULT_ROOM_VERSION}",
  131. )
  132. self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
  133. return channel.json_body
  134. def test_send_join(self) -> None:
  135. """happy-path test of send_join"""
  136. joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME
  137. join_result = self._make_join(joining_user)
  138. join_event_dict = join_result["event"]
  139. self.add_hashes_and_signatures_from_other_server(
  140. join_event_dict,
  141. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  142. )
  143. channel = self.make_signed_federation_request(
  144. "PUT",
  145. f"/_matrix/federation/v2/send_join/{self._room_id}/x",
  146. content=join_event_dict,
  147. )
  148. self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
  149. # we should get complete room state back
  150. returned_state = [
  151. (ev["type"], ev["state_key"]) for ev in channel.json_body["state"]
  152. ]
  153. self.assertCountEqual(
  154. returned_state,
  155. [
  156. ("m.room.create", ""),
  157. ("m.room.power_levels", ""),
  158. ("m.room.join_rules", ""),
  159. ("m.room.history_visibility", ""),
  160. ("m.room.member", "@kermit:test"),
  161. ("m.room.member", "@fozzie:test"),
  162. # nb: *not* the joining user
  163. ],
  164. )
  165. # also check the auth chain
  166. returned_auth_chain_events = [
  167. (ev["type"], ev["state_key"]) for ev in channel.json_body["auth_chain"]
  168. ]
  169. self.assertCountEqual(
  170. returned_auth_chain_events,
  171. [
  172. ("m.room.create", ""),
  173. ("m.room.member", "@kermit:test"),
  174. ("m.room.power_levels", ""),
  175. ("m.room.join_rules", ""),
  176. ],
  177. )
  178. # the room should show that the new user is a member
  179. r = self.get_success(
  180. self._storage_controllers.state.get_current_state(self._room_id)
  181. )
  182. self.assertEqual(r[("m.room.member", joining_user)].membership, "join")
  183. def test_send_join_partial_state(self) -> None:
  184. """/send_join should return partial state, if requested"""
  185. joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME
  186. join_result = self._make_join(joining_user)
  187. join_event_dict = join_result["event"]
  188. self.add_hashes_and_signatures_from_other_server(
  189. join_event_dict,
  190. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  191. )
  192. channel = self.make_signed_federation_request(
  193. "PUT",
  194. f"/_matrix/federation/v2/send_join/{self._room_id}/x?omit_members=true",
  195. content=join_event_dict,
  196. )
  197. self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
  198. # expect a reduced room state
  199. returned_state = [
  200. (ev["type"], ev["state_key"]) for ev in channel.json_body["state"]
  201. ]
  202. self.assertCountEqual(
  203. returned_state,
  204. [
  205. ("m.room.create", ""),
  206. ("m.room.power_levels", ""),
  207. ("m.room.join_rules", ""),
  208. ("m.room.history_visibility", ""),
  209. # Users included here because they're heroes.
  210. ("m.room.member", "@kermit:test"),
  211. ("m.room.member", "@fozzie:test"),
  212. ],
  213. )
  214. # the auth chain should not include anything already in "state"
  215. returned_auth_chain_events = [
  216. (ev["type"], ev["state_key"]) for ev in channel.json_body["auth_chain"]
  217. ]
  218. self.assertCountEqual(
  219. returned_auth_chain_events,
  220. # TODO: change the test so that we get at least one event in the auth chain
  221. # here.
  222. [],
  223. )
  224. # the room should show that the new user is a member
  225. r = self.get_success(
  226. self._storage_controllers.state.get_current_state(self._room_id)
  227. )
  228. self.assertEqual(r[("m.room.member", joining_user)].membership, "join")
  229. @override_config({"rc_joins_per_room": {"per_second": 0, "burst_count": 3}})
  230. def test_make_join_respects_room_join_rate_limit(self) -> None:
  231. # In the test setup, two users join the room. Since the rate limiter burst
  232. # count is 3, a new make_join request to the room should be accepted.
  233. joining_user = "@ronniecorbett:" + self.OTHER_SERVER_NAME
  234. self._make_join(joining_user)
  235. # Now have a new local user join the room. This saturates the rate limiter
  236. # bucket, so the next make_join should be denied.
  237. new_local_user = self.register_user("animal", "animal")
  238. token = self.login("animal", "animal")
  239. self.helper.join(self._room_id, new_local_user, tok=token)
  240. joining_user = "@ronniebarker:" + self.OTHER_SERVER_NAME
  241. channel = self.make_signed_federation_request(
  242. "GET",
  243. f"/_matrix/federation/v1/make_join/{self._room_id}/{joining_user}"
  244. f"?ver={DEFAULT_ROOM_VERSION}",
  245. )
  246. self.assertEqual(channel.code, HTTPStatus.TOO_MANY_REQUESTS, channel.json_body)
  247. @override_config({"rc_joins_per_room": {"per_second": 0, "burst_count": 3}})
  248. def test_send_join_contributes_to_room_join_rate_limit_and_is_limited(self) -> None:
  249. # Make two make_join requests up front. (These are rate limited, but do not
  250. # contribute to the rate limit.)
  251. join_event_dicts = []
  252. for i in range(2):
  253. joining_user = f"@misspiggy{i}:{self.OTHER_SERVER_NAME}"
  254. join_result = self._make_join(joining_user)
  255. join_event_dict = join_result["event"]
  256. self.add_hashes_and_signatures_from_other_server(
  257. join_event_dict,
  258. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  259. )
  260. join_event_dicts.append(join_event_dict)
  261. # In the test setup, two users join the room. Since the rate limiter burst
  262. # count is 3, the first send_join should be accepted...
  263. channel = self.make_signed_federation_request(
  264. "PUT",
  265. f"/_matrix/federation/v2/send_join/{self._room_id}/join0",
  266. content=join_event_dicts[0],
  267. )
  268. self.assertEqual(channel.code, 200, channel.json_body)
  269. # ... but the second should be denied.
  270. channel = self.make_signed_federation_request(
  271. "PUT",
  272. f"/_matrix/federation/v2/send_join/{self._room_id}/join1",
  273. content=join_event_dicts[1],
  274. )
  275. self.assertEqual(channel.code, HTTPStatus.TOO_MANY_REQUESTS, channel.json_body)
  276. # NB: we could write a test which checks that the send_join event is seen
  277. # by other workers over replication, and that they update their rate limit
  278. # buckets accordingly. I'm going to assume that the join event gets sent over
  279. # replication, at which point the tests.handlers.room_member test
  280. # test_local_users_joining_on_another_worker_contribute_to_rate_limit
  281. # is probably sufficient to reassure that the bucket is updated.
  282. def _create_acl_event(content: JsonDict) -> EventBase:
  283. return make_event_from_dict(
  284. {
  285. "room_id": "!a:b",
  286. "event_id": "$a:b",
  287. "type": "m.room.server_acls",
  288. "sender": "@a:b",
  289. "content": content,
  290. }
  291. )