You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

336 lines
13 KiB

  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.federation.federation_server import server_matches_acl_event
  23. from synapse.rest import admin
  24. from synapse.rest.client import login, room
  25. from synapse.server import HomeServer
  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_blacklisted_server(self) -> None:
  55. e = _create_acl_event({"allow": ["*"], "deny": ["evil.com"]})
  56. logging.info("ACL event: %s", e.content)
  57. self.assertFalse(server_matches_acl_event("evil.com", e))
  58. self.assertFalse(server_matches_acl_event("EVIL.COM", e))
  59. self.assertTrue(server_matches_acl_event("evil.com.au", e))
  60. self.assertTrue(server_matches_acl_event("honestly.not.evil.com", e))
  61. def test_block_ip_literals(self) -> None:
  62. e = _create_acl_event({"allow_ip_literals": False, "allow": ["*"]})
  63. logging.info("ACL event: %s", e.content)
  64. self.assertFalse(server_matches_acl_event("1.2.3.4", e))
  65. self.assertTrue(server_matches_acl_event("1a.2.3.4", e))
  66. self.assertFalse(server_matches_acl_event("[1:2::]", e))
  67. self.assertTrue(server_matches_acl_event("1:2:3:4", e))
  68. def test_wildcard_matching(self) -> None:
  69. e = _create_acl_event({"allow": ["good*.com"]})
  70. self.assertTrue(
  71. server_matches_acl_event("good.com", e),
  72. "* matches 0 characters",
  73. )
  74. self.assertTrue(
  75. server_matches_acl_event("GOOD.COM", e),
  76. "pattern is case-insensitive",
  77. )
  78. self.assertTrue(
  79. server_matches_acl_event("good.aa.com", e),
  80. "* matches several characters, including '.'",
  81. )
  82. self.assertFalse(
  83. server_matches_acl_event("ishgood.com", e),
  84. "pattern does not allow prefixes",
  85. )
  86. class StateQueryTests(unittest.FederatingHomeserverTestCase):
  87. servlets = [
  88. admin.register_servlets,
  89. room.register_servlets,
  90. login.register_servlets,
  91. ]
  92. def test_needs_to_be_in_room(self) -> None:
  93. """/v1/state/<room_id> requires the server to be in the room"""
  94. u1 = self.register_user("u1", "pass")
  95. u1_token = self.login("u1", "pass")
  96. room_1 = self.helper.create_room_as(u1, tok=u1_token)
  97. channel = self.make_signed_federation_request(
  98. "GET", "/_matrix/federation/v1/state/%s?event_id=xyz" % (room_1,)
  99. )
  100. self.assertEqual(HTTPStatus.FORBIDDEN, channel.code, channel.result)
  101. self.assertEqual(channel.json_body["errcode"], "M_FORBIDDEN")
  102. class SendJoinFederationTests(unittest.FederatingHomeserverTestCase):
  103. servlets = [
  104. admin.register_servlets,
  105. room.register_servlets,
  106. login.register_servlets,
  107. ]
  108. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  109. super().prepare(reactor, clock, hs)
  110. self._storage_controllers = hs.get_storage_controllers()
  111. # create the room
  112. creator_user_id = self.register_user("kermit", "test")
  113. tok = self.login("kermit", "test")
  114. self._room_id = self.helper.create_room_as(
  115. room_creator=creator_user_id, tok=tok
  116. )
  117. # a second member on the orgin HS
  118. second_member_user_id = self.register_user("fozzie", "bear")
  119. tok2 = self.login("fozzie", "bear")
  120. self.helper.join(self._room_id, second_member_user_id, tok=tok2)
  121. def _make_join(self, user_id: str) -> JsonDict:
  122. channel = self.make_signed_federation_request(
  123. "GET",
  124. f"/_matrix/federation/v1/make_join/{self._room_id}/{user_id}"
  125. f"?ver={DEFAULT_ROOM_VERSION}",
  126. )
  127. self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
  128. return channel.json_body
  129. def test_send_join(self) -> None:
  130. """happy-path test of send_join"""
  131. joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME
  132. join_result = self._make_join(joining_user)
  133. join_event_dict = join_result["event"]
  134. self.add_hashes_and_signatures_from_other_server(
  135. join_event_dict,
  136. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  137. )
  138. channel = self.make_signed_federation_request(
  139. "PUT",
  140. f"/_matrix/federation/v2/send_join/{self._room_id}/x",
  141. content=join_event_dict,
  142. )
  143. self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
  144. # we should get complete room state back
  145. returned_state = [
  146. (ev["type"], ev["state_key"]) for ev in channel.json_body["state"]
  147. ]
  148. self.assertCountEqual(
  149. returned_state,
  150. [
  151. ("m.room.create", ""),
  152. ("m.room.power_levels", ""),
  153. ("m.room.join_rules", ""),
  154. ("m.room.history_visibility", ""),
  155. ("m.room.member", "@kermit:test"),
  156. ("m.room.member", "@fozzie:test"),
  157. # nb: *not* the joining user
  158. ],
  159. )
  160. # also check the auth chain
  161. returned_auth_chain_events = [
  162. (ev["type"], ev["state_key"]) for ev in channel.json_body["auth_chain"]
  163. ]
  164. self.assertCountEqual(
  165. returned_auth_chain_events,
  166. [
  167. ("m.room.create", ""),
  168. ("m.room.member", "@kermit:test"),
  169. ("m.room.power_levels", ""),
  170. ("m.room.join_rules", ""),
  171. ],
  172. )
  173. # the room should show that the new user is a member
  174. r = self.get_success(
  175. self._storage_controllers.state.get_current_state(self._room_id)
  176. )
  177. self.assertEqual(r[("m.room.member", joining_user)].membership, "join")
  178. def test_send_join_partial_state(self) -> None:
  179. """/send_join should return partial state, if requested"""
  180. joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME
  181. join_result = self._make_join(joining_user)
  182. join_event_dict = join_result["event"]
  183. self.add_hashes_and_signatures_from_other_server(
  184. join_event_dict,
  185. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  186. )
  187. channel = self.make_signed_federation_request(
  188. "PUT",
  189. f"/_matrix/federation/v2/send_join/{self._room_id}/x?omit_members=true",
  190. content=join_event_dict,
  191. )
  192. self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
  193. # expect a reduced room state
  194. returned_state = [
  195. (ev["type"], ev["state_key"]) for ev in channel.json_body["state"]
  196. ]
  197. self.assertCountEqual(
  198. returned_state,
  199. [
  200. ("m.room.create", ""),
  201. ("m.room.power_levels", ""),
  202. ("m.room.join_rules", ""),
  203. ("m.room.history_visibility", ""),
  204. # Users included here because they're heroes.
  205. ("m.room.member", "@kermit:test"),
  206. ("m.room.member", "@fozzie:test"),
  207. ],
  208. )
  209. # the auth chain should not include anything already in "state"
  210. returned_auth_chain_events = [
  211. (ev["type"], ev["state_key"]) for ev in channel.json_body["auth_chain"]
  212. ]
  213. self.assertCountEqual(
  214. returned_auth_chain_events,
  215. # TODO: change the test so that we get at least one event in the auth chain
  216. # here.
  217. [],
  218. )
  219. # the room should show that the new user is a member
  220. r = self.get_success(
  221. self._storage_controllers.state.get_current_state(self._room_id)
  222. )
  223. self.assertEqual(r[("m.room.member", joining_user)].membership, "join")
  224. @override_config({"rc_joins_per_room": {"per_second": 0, "burst_count": 3}})
  225. def test_make_join_respects_room_join_rate_limit(self) -> None:
  226. # In the test setup, two users join the room. Since the rate limiter burst
  227. # count is 3, a new make_join request to the room should be accepted.
  228. joining_user = "@ronniecorbett:" + self.OTHER_SERVER_NAME
  229. self._make_join(joining_user)
  230. # Now have a new local user join the room. This saturates the rate limiter
  231. # bucket, so the next make_join should be denied.
  232. new_local_user = self.register_user("animal", "animal")
  233. token = self.login("animal", "animal")
  234. self.helper.join(self._room_id, new_local_user, tok=token)
  235. joining_user = "@ronniebarker:" + self.OTHER_SERVER_NAME
  236. channel = self.make_signed_federation_request(
  237. "GET",
  238. f"/_matrix/federation/v1/make_join/{self._room_id}/{joining_user}"
  239. f"?ver={DEFAULT_ROOM_VERSION}",
  240. )
  241. self.assertEqual(channel.code, HTTPStatus.TOO_MANY_REQUESTS, channel.json_body)
  242. @override_config({"rc_joins_per_room": {"per_second": 0, "burst_count": 3}})
  243. def test_send_join_contributes_to_room_join_rate_limit_and_is_limited(self) -> None:
  244. # Make two make_join requests up front. (These are rate limited, but do not
  245. # contribute to the rate limit.)
  246. join_event_dicts = []
  247. for i in range(2):
  248. joining_user = f"@misspiggy{i}:{self.OTHER_SERVER_NAME}"
  249. join_result = self._make_join(joining_user)
  250. join_event_dict = join_result["event"]
  251. self.add_hashes_and_signatures_from_other_server(
  252. join_event_dict,
  253. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  254. )
  255. join_event_dicts.append(join_event_dict)
  256. # In the test setup, two users join the room. Since the rate limiter burst
  257. # count is 3, the first send_join should be accepted...
  258. channel = self.make_signed_federation_request(
  259. "PUT",
  260. f"/_matrix/federation/v2/send_join/{self._room_id}/join0",
  261. content=join_event_dicts[0],
  262. )
  263. self.assertEqual(channel.code, 200, channel.json_body)
  264. # ... but the second should be denied.
  265. channel = self.make_signed_federation_request(
  266. "PUT",
  267. f"/_matrix/federation/v2/send_join/{self._room_id}/join1",
  268. content=join_event_dicts[1],
  269. )
  270. self.assertEqual(channel.code, HTTPStatus.TOO_MANY_REQUESTS, channel.json_body)
  271. # NB: we could write a test which checks that the send_join event is seen
  272. # by other workers over replication, and that they update their rate limit
  273. # buckets accordingly. I'm going to assume that the join event gets sent over
  274. # replication, at which point the tests.handlers.room_member test
  275. # test_local_users_joining_on_another_worker_contribute_to_rate_limit
  276. # is probably sufficient to reassure that the bucket is updated.
  277. def _create_acl_event(content: JsonDict) -> EventBase:
  278. return make_event_from_dict(
  279. {
  280. "room_id": "!a:b",
  281. "event_id": "$a:b",
  282. "type": "m.room.server_acls",
  283. "sender": "@a:b",
  284. "content": content,
  285. }
  286. )