test_federation.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. # Copyright 2019 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 logging
  15. from unittest import TestCase
  16. from synapse.api.constants import EventTypes
  17. from synapse.api.errors import AuthError, Codes, LimitExceededError, SynapseError
  18. from synapse.api.room_versions import RoomVersions
  19. from synapse.events import EventBase
  20. from synapse.federation.federation_base import event_from_pdu_json
  21. from synapse.logging.context import LoggingContext, run_in_background
  22. from synapse.rest import admin
  23. from synapse.rest.client.v1 import login, room
  24. from tests import unittest
  25. logger = logging.getLogger(__name__)
  26. class FederationTestCase(unittest.HomeserverTestCase):
  27. servlets = [
  28. admin.register_servlets,
  29. login.register_servlets,
  30. room.register_servlets,
  31. ]
  32. def make_homeserver(self, reactor, clock):
  33. hs = self.setup_test_homeserver(federation_http_client=None)
  34. self.handler = hs.get_federation_handler()
  35. self.store = hs.get_datastore()
  36. return hs
  37. def test_exchange_revoked_invite(self):
  38. user_id = self.register_user("kermit", "test")
  39. tok = self.login("kermit", "test")
  40. room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
  41. # Send a 3PID invite event with an empty body so it's considered as a revoked one.
  42. invite_token = "sometoken"
  43. self.helper.send_state(
  44. room_id=room_id,
  45. event_type=EventTypes.ThirdPartyInvite,
  46. state_key=invite_token,
  47. body={},
  48. tok=tok,
  49. )
  50. d = self.handler.on_exchange_third_party_invite_request(
  51. event_dict={
  52. "type": EventTypes.Member,
  53. "room_id": room_id,
  54. "sender": user_id,
  55. "state_key": "@someone:example.org",
  56. "content": {
  57. "membership": "invite",
  58. "third_party_invite": {
  59. "display_name": "alice",
  60. "signed": {
  61. "mxid": "@alice:localhost",
  62. "token": invite_token,
  63. "signatures": {
  64. "magic.forest": {
  65. "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
  66. }
  67. },
  68. },
  69. },
  70. },
  71. },
  72. )
  73. failure = self.get_failure(d, AuthError).value
  74. self.assertEqual(failure.code, 403, failure)
  75. self.assertEqual(failure.errcode, Codes.FORBIDDEN, failure)
  76. self.assertEqual(failure.msg, "You are not invited to this room.")
  77. def test_rejected_message_event_state(self):
  78. """
  79. Check that we store the state group correctly for rejected non-state events.
  80. Regression test for #6289.
  81. """
  82. OTHER_SERVER = "otherserver"
  83. OTHER_USER = "@otheruser:" + OTHER_SERVER
  84. # create the room
  85. user_id = self.register_user("kermit", "test")
  86. tok = self.login("kermit", "test")
  87. room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
  88. room_version = self.get_success(self.store.get_room_version(room_id))
  89. # pretend that another server has joined
  90. join_event = self._build_and_send_join_event(OTHER_SERVER, OTHER_USER, room_id)
  91. # check the state group
  92. sg = self.successResultOf(
  93. self.store._get_state_group_for_event(join_event.event_id)
  94. )
  95. # build and send an event which will be rejected
  96. ev = event_from_pdu_json(
  97. {
  98. "type": EventTypes.Message,
  99. "content": {},
  100. "room_id": room_id,
  101. "sender": "@yetanotheruser:" + OTHER_SERVER,
  102. "depth": join_event["depth"] + 1,
  103. "prev_events": [join_event.event_id],
  104. "auth_events": [],
  105. "origin_server_ts": self.clock.time_msec(),
  106. },
  107. room_version,
  108. )
  109. with LoggingContext("send_rejected"):
  110. d = run_in_background(self.handler.on_receive_pdu, OTHER_SERVER, ev)
  111. self.get_success(d)
  112. # that should have been rejected
  113. e = self.get_success(self.store.get_event(ev.event_id, allow_rejected=True))
  114. self.assertIsNotNone(e.rejected_reason)
  115. # ... and the state group should be the same as before
  116. sg2 = self.successResultOf(self.store._get_state_group_for_event(ev.event_id))
  117. self.assertEqual(sg, sg2)
  118. def test_rejected_state_event_state(self):
  119. """
  120. Check that we store the state group correctly for rejected state events.
  121. Regression test for #6289.
  122. """
  123. OTHER_SERVER = "otherserver"
  124. OTHER_USER = "@otheruser:" + OTHER_SERVER
  125. # create the room
  126. user_id = self.register_user("kermit", "test")
  127. tok = self.login("kermit", "test")
  128. room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
  129. room_version = self.get_success(self.store.get_room_version(room_id))
  130. # pretend that another server has joined
  131. join_event = self._build_and_send_join_event(OTHER_SERVER, OTHER_USER, room_id)
  132. # check the state group
  133. sg = self.successResultOf(
  134. self.store._get_state_group_for_event(join_event.event_id)
  135. )
  136. # build and send an event which will be rejected
  137. ev = event_from_pdu_json(
  138. {
  139. "type": "org.matrix.test",
  140. "state_key": "test_key",
  141. "content": {},
  142. "room_id": room_id,
  143. "sender": "@yetanotheruser:" + OTHER_SERVER,
  144. "depth": join_event["depth"] + 1,
  145. "prev_events": [join_event.event_id],
  146. "auth_events": [],
  147. "origin_server_ts": self.clock.time_msec(),
  148. },
  149. room_version,
  150. )
  151. with LoggingContext("send_rejected"):
  152. d = run_in_background(self.handler.on_receive_pdu, OTHER_SERVER, ev)
  153. self.get_success(d)
  154. # that should have been rejected
  155. e = self.get_success(self.store.get_event(ev.event_id, allow_rejected=True))
  156. self.assertIsNotNone(e.rejected_reason)
  157. # ... and the state group should be the same as before
  158. sg2 = self.successResultOf(self.store._get_state_group_for_event(ev.event_id))
  159. self.assertEqual(sg, sg2)
  160. @unittest.override_config(
  161. {"rc_invites": {"per_user": {"per_second": 0.5, "burst_count": 3}}}
  162. )
  163. def test_invite_by_user_ratelimit(self):
  164. """Tests that invites from federation to a particular user are
  165. actually rate-limited.
  166. """
  167. other_server = "otherserver"
  168. other_user = "@otheruser:" + other_server
  169. # create the room
  170. user_id = self.register_user("kermit", "test")
  171. tok = self.login("kermit", "test")
  172. def create_invite():
  173. room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
  174. room_version = self.get_success(self.store.get_room_version(room_id))
  175. return event_from_pdu_json(
  176. {
  177. "type": EventTypes.Member,
  178. "content": {"membership": "invite"},
  179. "room_id": room_id,
  180. "sender": other_user,
  181. "state_key": "@user:test",
  182. "depth": 32,
  183. "prev_events": [],
  184. "auth_events": [],
  185. "origin_server_ts": self.clock.time_msec(),
  186. },
  187. room_version,
  188. )
  189. for _ in range(3):
  190. event = create_invite()
  191. self.get_success(
  192. self.handler.on_invite_request(
  193. other_server,
  194. event,
  195. event.room_version,
  196. )
  197. )
  198. event = create_invite()
  199. self.get_failure(
  200. self.handler.on_invite_request(
  201. other_server,
  202. event,
  203. event.room_version,
  204. ),
  205. exc=LimitExceededError,
  206. )
  207. def _build_and_send_join_event(self, other_server, other_user, room_id):
  208. join_event = self.get_success(
  209. self.handler.on_make_join_request(other_server, room_id, other_user)
  210. )
  211. # the auth code requires that a signature exists, but doesn't check that
  212. # signature... go figure.
  213. join_event.signatures[other_server] = {"x": "y"}
  214. with LoggingContext("send_join"):
  215. d = run_in_background(
  216. self.handler.on_send_membership_event, other_server, join_event
  217. )
  218. self.get_success(d)
  219. # sanity-check: the room should show that the new user is a member
  220. r = self.get_success(self.store.get_current_state_ids(room_id))
  221. self.assertEqual(r[(EventTypes.Member, other_user)], join_event.event_id)
  222. return join_event
  223. class EventFromPduTestCase(TestCase):
  224. def test_valid_json(self):
  225. """Valid JSON should be turned into an event."""
  226. ev = event_from_pdu_json(
  227. {
  228. "type": EventTypes.Message,
  229. "content": {"bool": True, "null": None, "int": 1, "str": "foobar"},
  230. "room_id": "!room:test",
  231. "sender": "@user:test",
  232. "depth": 1,
  233. "prev_events": [],
  234. "auth_events": [],
  235. "origin_server_ts": 1234,
  236. },
  237. RoomVersions.V6,
  238. )
  239. self.assertIsInstance(ev, EventBase)
  240. def test_invalid_numbers(self):
  241. """Invalid values for an integer should be rejected, all floats should be rejected."""
  242. for value in [
  243. -(2 ** 53),
  244. 2 ** 53,
  245. 1.0,
  246. float("inf"),
  247. float("-inf"),
  248. float("nan"),
  249. ]:
  250. with self.assertRaises(SynapseError):
  251. event_from_pdu_json(
  252. {
  253. "type": EventTypes.Message,
  254. "content": {"foo": value},
  255. "room_id": "!room:test",
  256. "sender": "@user:test",
  257. "depth": 1,
  258. "prev_events": [],
  259. "auth_events": [],
  260. "origin_server_ts": 1234,
  261. },
  262. RoomVersions.V6,
  263. )
  264. def test_invalid_nested(self):
  265. """List and dictionaries are recursively searched."""
  266. with self.assertRaises(SynapseError):
  267. event_from_pdu_json(
  268. {
  269. "type": EventTypes.Message,
  270. "content": {"foo": [{"bar": 2 ** 56}]},
  271. "room_id": "!room:test",
  272. "sender": "@user:test",
  273. "depth": 1,
  274. "prev_events": [],
  275. "auth_events": [],
  276. "origin_server_ts": 1234,
  277. },
  278. RoomVersions.V6,
  279. )