test_federation_server.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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 parameterized import parameterized
  17. from twisted.test.proto_helpers import MemoryReactor
  18. from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
  19. from synapse.config.server import DEFAULT_ROOM_VERSION
  20. from synapse.crypto.event_signing import add_hashes_and_signatures
  21. from synapse.events import 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):
  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(400, 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):
  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):
  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):
  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_without_event_id(self):
  93. """
  94. Querying v1/state/<room_id> without an event ID will return the current
  95. known state.
  96. """
  97. u1 = self.register_user("u1", "pass")
  98. u1_token = self.login("u1", "pass")
  99. room_1 = self.helper.create_room_as(u1, tok=u1_token)
  100. self.inject_room_member(room_1, "@user:other.example.com", "join")
  101. channel = self.make_signed_federation_request(
  102. "GET", "/_matrix/federation/v1/state/%s" % (room_1,)
  103. )
  104. self.assertEqual(200, channel.code, channel.result)
  105. self.assertEqual(
  106. channel.json_body["room_version"],
  107. self.hs.config.server.default_room_version.identifier,
  108. )
  109. members = set(
  110. map(
  111. lambda x: x["state_key"],
  112. filter(
  113. lambda x: x["type"] == "m.room.member", channel.json_body["pdus"]
  114. ),
  115. )
  116. )
  117. self.assertEqual(members, {"@user:other.example.com", u1})
  118. self.assertEqual(len(channel.json_body["pdus"]), 6)
  119. def test_needs_to_be_in_room(self):
  120. """
  121. Querying v1/state/<room_id> requires the server
  122. be in the room to provide data.
  123. """
  124. u1 = self.register_user("u1", "pass")
  125. u1_token = self.login("u1", "pass")
  126. room_1 = self.helper.create_room_as(u1, tok=u1_token)
  127. channel = self.make_signed_federation_request(
  128. "GET", "/_matrix/federation/v1/state/%s" % (room_1,)
  129. )
  130. self.assertEqual(403, channel.code, channel.result)
  131. self.assertEqual(channel.json_body["errcode"], "M_FORBIDDEN")
  132. class SendJoinFederationTests(unittest.FederatingHomeserverTestCase):
  133. servlets = [
  134. admin.register_servlets,
  135. room.register_servlets,
  136. login.register_servlets,
  137. ]
  138. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer):
  139. super().prepare(reactor, clock, hs)
  140. # create the room
  141. creator_user_id = self.register_user("kermit", "test")
  142. tok = self.login("kermit", "test")
  143. self._room_id = self.helper.create_room_as(
  144. room_creator=creator_user_id, tok=tok
  145. )
  146. # a second member on the orgin HS
  147. second_member_user_id = self.register_user("fozzie", "bear")
  148. tok2 = self.login("fozzie", "bear")
  149. self.helper.join(self._room_id, second_member_user_id, tok=tok2)
  150. def _make_join(self, user_id) -> JsonDict:
  151. channel = self.make_signed_federation_request(
  152. "GET",
  153. f"/_matrix/federation/v1/make_join/{self._room_id}/{user_id}"
  154. f"?ver={DEFAULT_ROOM_VERSION}",
  155. )
  156. self.assertEqual(channel.code, 200, channel.json_body)
  157. return channel.json_body
  158. def test_send_join(self):
  159. """happy-path test of send_join"""
  160. joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME
  161. join_result = self._make_join(joining_user)
  162. join_event_dict = join_result["event"]
  163. add_hashes_and_signatures(
  164. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  165. join_event_dict,
  166. signature_name=self.OTHER_SERVER_NAME,
  167. signing_key=self.OTHER_SERVER_SIGNATURE_KEY,
  168. )
  169. channel = self.make_signed_federation_request(
  170. "PUT",
  171. f"/_matrix/federation/v2/send_join/{self._room_id}/x",
  172. content=join_event_dict,
  173. )
  174. self.assertEqual(channel.code, 200, channel.json_body)
  175. # we should get complete room state back
  176. returned_state = [
  177. (ev["type"], ev["state_key"]) for ev in channel.json_body["state"]
  178. ]
  179. self.assertCountEqual(
  180. returned_state,
  181. [
  182. ("m.room.create", ""),
  183. ("m.room.power_levels", ""),
  184. ("m.room.join_rules", ""),
  185. ("m.room.history_visibility", ""),
  186. ("m.room.member", "@kermit:test"),
  187. ("m.room.member", "@fozzie:test"),
  188. # nb: *not* the joining user
  189. ],
  190. )
  191. # also check the auth chain
  192. returned_auth_chain_events = [
  193. (ev["type"], ev["state_key"]) for ev in channel.json_body["auth_chain"]
  194. ]
  195. self.assertCountEqual(
  196. returned_auth_chain_events,
  197. [
  198. ("m.room.create", ""),
  199. ("m.room.member", "@kermit:test"),
  200. ("m.room.power_levels", ""),
  201. ("m.room.join_rules", ""),
  202. ],
  203. )
  204. # the room should show that the new user is a member
  205. r = self.get_success(
  206. self.hs.get_state_handler().get_current_state(self._room_id)
  207. )
  208. self.assertEqual(r[("m.room.member", joining_user)].membership, "join")
  209. @override_config({"experimental_features": {"msc3706_enabled": True}})
  210. def test_send_join_partial_state(self):
  211. """When MSC3706 support is enabled, /send_join should return partial state"""
  212. joining_user = "@misspiggy:" + self.OTHER_SERVER_NAME
  213. join_result = self._make_join(joining_user)
  214. join_event_dict = join_result["event"]
  215. add_hashes_and_signatures(
  216. KNOWN_ROOM_VERSIONS[DEFAULT_ROOM_VERSION],
  217. join_event_dict,
  218. signature_name=self.OTHER_SERVER_NAME,
  219. signing_key=self.OTHER_SERVER_SIGNATURE_KEY,
  220. )
  221. channel = self.make_signed_federation_request(
  222. "PUT",
  223. f"/_matrix/federation/v2/send_join/{self._room_id}/x?org.matrix.msc3706.partial_state=true",
  224. content=join_event_dict,
  225. )
  226. self.assertEqual(channel.code, 200, channel.json_body)
  227. # expect a reduced room state
  228. returned_state = [
  229. (ev["type"], ev["state_key"]) for ev in channel.json_body["state"]
  230. ]
  231. self.assertCountEqual(
  232. returned_state,
  233. [
  234. ("m.room.create", ""),
  235. ("m.room.power_levels", ""),
  236. ("m.room.join_rules", ""),
  237. ("m.room.history_visibility", ""),
  238. ],
  239. )
  240. # the auth chain should not include anything already in "state"
  241. returned_auth_chain_events = [
  242. (ev["type"], ev["state_key"]) for ev in channel.json_body["auth_chain"]
  243. ]
  244. self.assertCountEqual(
  245. returned_auth_chain_events,
  246. [
  247. ("m.room.member", "@kermit:test"),
  248. ],
  249. )
  250. # the room should show that the new user is a member
  251. r = self.get_success(
  252. self.hs.get_state_handler().get_current_state(self._room_id)
  253. )
  254. self.assertEqual(r[("m.room.member", joining_user)].membership, "join")
  255. def _create_acl_event(content):
  256. return make_event_from_dict(
  257. {
  258. "room_id": "!a:b",
  259. "event_id": "$a:b",
  260. "type": "m.room.server_acls",
  261. "sender": "@a:b",
  262. "content": content,
  263. }
  264. )