test_redaction.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014-2016 OpenMarket Ltd
  3. # Copyright 2019 The Matrix.org Foundation C.I.C.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. from mock import Mock
  17. from twisted.internet import defer
  18. from synapse.api.constants import EventTypes, Membership
  19. from synapse.api.room_versions import RoomVersions
  20. from synapse.types import RoomID, UserID
  21. from tests import unittest
  22. from tests.utils import create_room
  23. class RedactionTestCase(unittest.HomeserverTestCase):
  24. def make_homeserver(self, reactor, clock):
  25. return self.setup_test_homeserver(
  26. resource_for_federation=Mock(), http_client=None
  27. )
  28. def prepare(self, reactor, clock, hs):
  29. self.store = hs.get_datastore()
  30. self.event_builder_factory = hs.get_event_builder_factory()
  31. self.event_creation_handler = hs.get_event_creation_handler()
  32. self.u_alice = UserID.from_string("@alice:test")
  33. self.u_bob = UserID.from_string("@bob:test")
  34. self.room1 = RoomID.from_string("!abc123:test")
  35. self.get_success(
  36. create_room(hs, self.room1.to_string(), self.u_alice.to_string())
  37. )
  38. self.depth = 1
  39. def inject_room_member(
  40. self, room, user, membership, replaces_state=None, extra_content={}
  41. ):
  42. content = {"membership": membership}
  43. content.update(extra_content)
  44. builder = self.event_builder_factory.for_room_version(
  45. RoomVersions.V1,
  46. {
  47. "type": EventTypes.Member,
  48. "sender": user.to_string(),
  49. "state_key": user.to_string(),
  50. "room_id": room.to_string(),
  51. "content": content,
  52. },
  53. )
  54. event, context = self.get_success(
  55. self.event_creation_handler.create_new_client_event(builder)
  56. )
  57. self.get_success(self.store.persist_event(event, context))
  58. return event
  59. def inject_message(self, room, user, body):
  60. self.depth += 1
  61. builder = self.event_builder_factory.for_room_version(
  62. RoomVersions.V1,
  63. {
  64. "type": EventTypes.Message,
  65. "sender": user.to_string(),
  66. "state_key": user.to_string(),
  67. "room_id": room.to_string(),
  68. "content": {"body": body, "msgtype": "message"},
  69. },
  70. )
  71. event, context = self.get_success(
  72. self.event_creation_handler.create_new_client_event(builder)
  73. )
  74. self.get_success(self.store.persist_event(event, context))
  75. return event
  76. def inject_redaction(self, room, event_id, user, reason):
  77. builder = self.event_builder_factory.for_room_version(
  78. RoomVersions.V1,
  79. {
  80. "type": EventTypes.Redaction,
  81. "sender": user.to_string(),
  82. "state_key": user.to_string(),
  83. "room_id": room.to_string(),
  84. "content": {"reason": reason},
  85. "redacts": event_id,
  86. },
  87. )
  88. event, context = self.get_success(
  89. self.event_creation_handler.create_new_client_event(builder)
  90. )
  91. self.get_success(self.store.persist_event(event, context))
  92. def test_redact(self):
  93. self.get_success(
  94. self.inject_room_member(self.room1, self.u_alice, Membership.JOIN)
  95. )
  96. msg_event = self.get_success(self.inject_message(self.room1, self.u_alice, "t"))
  97. # Check event has not been redacted:
  98. event = self.get_success(self.store.get_event(msg_event.event_id))
  99. self.assertObjectHasAttributes(
  100. {
  101. "type": EventTypes.Message,
  102. "user_id": self.u_alice.to_string(),
  103. "content": {"body": "t", "msgtype": "message"},
  104. },
  105. event,
  106. )
  107. self.assertFalse("redacted_because" in event.unsigned)
  108. # Redact event
  109. reason = "Because I said so"
  110. self.get_success(
  111. self.inject_redaction(self.room1, msg_event.event_id, self.u_alice, reason)
  112. )
  113. event = self.get_success(self.store.get_event(msg_event.event_id))
  114. self.assertEqual(msg_event.event_id, event.event_id)
  115. self.assertTrue("redacted_because" in event.unsigned)
  116. self.assertObjectHasAttributes(
  117. {
  118. "type": EventTypes.Message,
  119. "user_id": self.u_alice.to_string(),
  120. "content": {},
  121. },
  122. event,
  123. )
  124. self.assertObjectHasAttributes(
  125. {
  126. "type": EventTypes.Redaction,
  127. "user_id": self.u_alice.to_string(),
  128. "content": {"reason": reason},
  129. },
  130. event.unsigned["redacted_because"],
  131. )
  132. def test_redact_join(self):
  133. self.get_success(
  134. self.inject_room_member(self.room1, self.u_alice, Membership.JOIN)
  135. )
  136. msg_event = self.get_success(
  137. self.inject_room_member(
  138. self.room1, self.u_bob, Membership.JOIN, extra_content={"blue": "red"}
  139. )
  140. )
  141. event = self.get_success(self.store.get_event(msg_event.event_id))
  142. self.assertObjectHasAttributes(
  143. {
  144. "type": EventTypes.Member,
  145. "user_id": self.u_bob.to_string(),
  146. "content": {"membership": Membership.JOIN, "blue": "red"},
  147. },
  148. event,
  149. )
  150. self.assertFalse(hasattr(event, "redacted_because"))
  151. # Redact event
  152. reason = "Because I said so"
  153. self.get_success(
  154. self.inject_redaction(self.room1, msg_event.event_id, self.u_alice, reason)
  155. )
  156. # Check redaction
  157. event = self.get_success(self.store.get_event(msg_event.event_id))
  158. self.assertTrue("redacted_because" in event.unsigned)
  159. self.assertObjectHasAttributes(
  160. {
  161. "type": EventTypes.Member,
  162. "user_id": self.u_bob.to_string(),
  163. "content": {"membership": Membership.JOIN},
  164. },
  165. event,
  166. )
  167. self.assertObjectHasAttributes(
  168. {
  169. "type": EventTypes.Redaction,
  170. "user_id": self.u_alice.to_string(),
  171. "content": {"reason": reason},
  172. },
  173. event.unsigned["redacted_because"],
  174. )
  175. def test_circular_redaction(self):
  176. redaction_event_id1 = "$redaction1_id:test"
  177. redaction_event_id2 = "$redaction2_id:test"
  178. class EventIdManglingBuilder:
  179. def __init__(self, base_builder, event_id):
  180. self._base_builder = base_builder
  181. self._event_id = event_id
  182. @defer.inlineCallbacks
  183. def build(self, prev_event_ids):
  184. built_event = yield self._base_builder.build(prev_event_ids)
  185. built_event.event_id = self._event_id
  186. built_event._event_dict["event_id"] = self._event_id
  187. return built_event
  188. @property
  189. def room_id(self):
  190. return self._base_builder.room_id
  191. event_1, context_1 = self.get_success(
  192. self.event_creation_handler.create_new_client_event(
  193. EventIdManglingBuilder(
  194. self.event_builder_factory.for_room_version(
  195. RoomVersions.V1,
  196. {
  197. "type": EventTypes.Redaction,
  198. "sender": self.u_alice.to_string(),
  199. "room_id": self.room1.to_string(),
  200. "content": {"reason": "test"},
  201. "redacts": redaction_event_id2,
  202. },
  203. ),
  204. redaction_event_id1,
  205. )
  206. )
  207. )
  208. self.get_success(self.store.persist_event(event_1, context_1))
  209. event_2, context_2 = self.get_success(
  210. self.event_creation_handler.create_new_client_event(
  211. EventIdManglingBuilder(
  212. self.event_builder_factory.for_room_version(
  213. RoomVersions.V1,
  214. {
  215. "type": EventTypes.Redaction,
  216. "sender": self.u_alice.to_string(),
  217. "room_id": self.room1.to_string(),
  218. "content": {"reason": "test"},
  219. "redacts": redaction_event_id1,
  220. },
  221. ),
  222. redaction_event_id2,
  223. )
  224. )
  225. )
  226. self.get_success(self.store.persist_event(event_2, context_2))
  227. # fetch one of the redactions
  228. fetched = self.get_success(self.store.get_event(redaction_event_id1))
  229. # it should have been redacted
  230. self.assertEqual(fetched.unsigned["redacted_by"], redaction_event_id2)
  231. self.assertEqual(
  232. fetched.unsigned["redacted_because"].event_id, redaction_event_id2
  233. )