test_email.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. # Copyright 2018 New Vector
  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 os
  15. import attr
  16. import pkg_resources
  17. from twisted.internet.defer import Deferred
  18. import synapse.rest.admin
  19. from synapse.api.errors import Codes, SynapseError
  20. from synapse.rest.client.v1 import login, room
  21. from tests.unittest import HomeserverTestCase
  22. @attr.s
  23. class _User:
  24. "Helper wrapper for user ID and access token"
  25. id = attr.ib()
  26. token = attr.ib()
  27. class EmailPusherTests(HomeserverTestCase):
  28. servlets = [
  29. synapse.rest.admin.register_servlets_for_client_rest_resource,
  30. room.register_servlets,
  31. login.register_servlets,
  32. ]
  33. user_id = True
  34. hijack_auth = False
  35. def make_homeserver(self, reactor, clock):
  36. # List[Tuple[Deferred, args, kwargs]]
  37. self.email_attempts = []
  38. def sendmail(*args, **kwargs):
  39. d = Deferred()
  40. self.email_attempts.append((d, args, kwargs))
  41. return d
  42. config = self.default_config()
  43. config["email"] = {
  44. "enable_notifs": True,
  45. "template_dir": os.path.abspath(
  46. pkg_resources.resource_filename("synapse", "res/templates")
  47. ),
  48. "expiry_template_html": "notice_expiry.html",
  49. "expiry_template_text": "notice_expiry.txt",
  50. "notif_template_html": "notif_mail.html",
  51. "notif_template_text": "notif_mail.txt",
  52. "smtp_host": "127.0.0.1",
  53. "smtp_port": 20,
  54. "require_transport_security": False,
  55. "smtp_user": None,
  56. "smtp_pass": None,
  57. "app_name": "Matrix",
  58. "notif_from": "test@example.com",
  59. "riot_base_url": None,
  60. }
  61. config["public_baseurl"] = "aaa"
  62. config["start_pushers"] = True
  63. hs = self.setup_test_homeserver(config=config, sendmail=sendmail)
  64. return hs
  65. def prepare(self, reactor, clock, hs):
  66. # Register the user who gets notified
  67. self.user_id = self.register_user("user", "pass")
  68. self.access_token = self.login("user", "pass")
  69. # Register other users
  70. self.others = [
  71. _User(
  72. id=self.register_user("otheruser1", "pass"),
  73. token=self.login("otheruser1", "pass"),
  74. ),
  75. _User(
  76. id=self.register_user("otheruser2", "pass"),
  77. token=self.login("otheruser2", "pass"),
  78. ),
  79. ]
  80. # Register the pusher
  81. user_tuple = self.get_success(
  82. self.hs.get_datastore().get_user_by_access_token(self.access_token)
  83. )
  84. self.token_id = user_tuple.token_id
  85. # We need to add email to account before we can create a pusher.
  86. self.get_success(
  87. hs.get_datastore().user_add_threepid(
  88. self.user_id, "email", "a@example.com", 0, 0
  89. )
  90. )
  91. self.pusher = self.get_success(
  92. self.hs.get_pusherpool().add_pusher(
  93. user_id=self.user_id,
  94. access_token=self.token_id,
  95. kind="email",
  96. app_id="m.email",
  97. app_display_name="Email Notifications",
  98. device_display_name="a@example.com",
  99. pushkey="a@example.com",
  100. lang=None,
  101. data={},
  102. )
  103. )
  104. def test_need_validated_email(self):
  105. """Test that we can only add an email pusher if the user has validated
  106. their email.
  107. """
  108. with self.assertRaises(SynapseError) as cm:
  109. self.get_success_or_raise(
  110. self.hs.get_pusherpool().add_pusher(
  111. user_id=self.user_id,
  112. access_token=self.token_id,
  113. kind="email",
  114. app_id="m.email",
  115. app_display_name="Email Notifications",
  116. device_display_name="b@example.com",
  117. pushkey="b@example.com",
  118. lang=None,
  119. data={},
  120. )
  121. )
  122. self.assertEqual(400, cm.exception.code)
  123. self.assertEqual(Codes.THREEPID_NOT_FOUND, cm.exception.errcode)
  124. def test_simple_sends_email(self):
  125. # Create a simple room with two users
  126. room = self.helper.create_room_as(self.user_id, tok=self.access_token)
  127. self.helper.invite(
  128. room=room, src=self.user_id, tok=self.access_token, targ=self.others[0].id
  129. )
  130. self.helper.join(room=room, user=self.others[0].id, tok=self.others[0].token)
  131. # The other user sends a single message.
  132. self.helper.send(room, body="Hi!", tok=self.others[0].token)
  133. # We should get emailed about that message
  134. self._check_for_mail()
  135. # The other user sends multiple messages.
  136. self.helper.send(room, body="Hi!", tok=self.others[0].token)
  137. self.helper.send(room, body="There!", tok=self.others[0].token)
  138. self._check_for_mail()
  139. def test_invite_sends_email(self):
  140. # Create a room and invite the user to it
  141. room = self.helper.create_room_as(self.others[0].id, tok=self.others[0].token)
  142. self.helper.invite(
  143. room=room,
  144. src=self.others[0].id,
  145. tok=self.others[0].token,
  146. targ=self.user_id,
  147. )
  148. # We should get emailed about the invite
  149. self._check_for_mail()
  150. def test_invite_to_empty_room_sends_email(self):
  151. # Create a room and invite the user to it
  152. room = self.helper.create_room_as(self.others[0].id, tok=self.others[0].token)
  153. self.helper.invite(
  154. room=room,
  155. src=self.others[0].id,
  156. tok=self.others[0].token,
  157. targ=self.user_id,
  158. )
  159. # Then have the original user leave
  160. self.helper.leave(room, self.others[0].id, tok=self.others[0].token)
  161. # We should get emailed about the invite
  162. self._check_for_mail()
  163. def test_multiple_members_email(self):
  164. # We want to test multiple notifications, so we pause processing of push
  165. # while we send messages.
  166. self.pusher._pause_processing()
  167. # Create a simple room with multiple other users
  168. room = self.helper.create_room_as(self.user_id, tok=self.access_token)
  169. for other in self.others:
  170. self.helper.invite(
  171. room=room, src=self.user_id, tok=self.access_token, targ=other.id
  172. )
  173. self.helper.join(room=room, user=other.id, tok=other.token)
  174. # The other users send some messages
  175. self.helper.send(room, body="Hi!", tok=self.others[0].token)
  176. self.helper.send(room, body="There!", tok=self.others[1].token)
  177. self.helper.send(room, body="There!", tok=self.others[1].token)
  178. # Nothing should have happened yet, as we're paused.
  179. assert not self.email_attempts
  180. self.pusher._resume_processing()
  181. # We should get emailed about those messages
  182. self._check_for_mail()
  183. def test_multiple_rooms(self):
  184. # We want to test multiple notifications from multiple rooms, so we pause
  185. # processing of push while we send messages.
  186. self.pusher._pause_processing()
  187. # Create a simple room with multiple other users
  188. rooms = [
  189. self.helper.create_room_as(self.user_id, tok=self.access_token),
  190. self.helper.create_room_as(self.user_id, tok=self.access_token),
  191. ]
  192. for r, other in zip(rooms, self.others):
  193. self.helper.invite(
  194. room=r, src=self.user_id, tok=self.access_token, targ=other.id
  195. )
  196. self.helper.join(room=r, user=other.id, tok=other.token)
  197. # The other users send some messages
  198. self.helper.send(rooms[0], body="Hi!", tok=self.others[0].token)
  199. self.helper.send(rooms[1], body="There!", tok=self.others[1].token)
  200. self.helper.send(rooms[1], body="There!", tok=self.others[1].token)
  201. # Nothing should have happened yet, as we're paused.
  202. assert not self.email_attempts
  203. self.pusher._resume_processing()
  204. # We should get emailed about those messages
  205. self._check_for_mail()
  206. def test_empty_room(self):
  207. """All users leaving a room shouldn't cause the pusher to break."""
  208. # Create a simple room with two users
  209. room = self.helper.create_room_as(self.user_id, tok=self.access_token)
  210. self.helper.invite(
  211. room=room, src=self.user_id, tok=self.access_token, targ=self.others[0].id
  212. )
  213. self.helper.join(room=room, user=self.others[0].id, tok=self.others[0].token)
  214. # The other user sends a single message.
  215. self.helper.send(room, body="Hi!", tok=self.others[0].token)
  216. # Leave the room before the message is processed.
  217. self.helper.leave(room, self.user_id, tok=self.access_token)
  218. self.helper.leave(room, self.others[0].id, tok=self.others[0].token)
  219. # We should get emailed about that message
  220. self._check_for_mail()
  221. def test_empty_room_multiple_messages(self):
  222. """All users leaving a room shouldn't cause the pusher to break."""
  223. # Create a simple room with two users
  224. room = self.helper.create_room_as(self.user_id, tok=self.access_token)
  225. self.helper.invite(
  226. room=room, src=self.user_id, tok=self.access_token, targ=self.others[0].id
  227. )
  228. self.helper.join(room=room, user=self.others[0].id, tok=self.others[0].token)
  229. # The other user sends a single message.
  230. self.helper.send(room, body="Hi!", tok=self.others[0].token)
  231. self.helper.send(room, body="There!", tok=self.others[0].token)
  232. # Leave the room before the message is processed.
  233. self.helper.leave(room, self.user_id, tok=self.access_token)
  234. self.helper.leave(room, self.others[0].id, tok=self.others[0].token)
  235. # We should get emailed about that message
  236. self._check_for_mail()
  237. def test_encrypted_message(self):
  238. room = self.helper.create_room_as(self.user_id, tok=self.access_token)
  239. self.helper.invite(
  240. room=room, src=self.user_id, tok=self.access_token, targ=self.others[0].id
  241. )
  242. self.helper.join(room=room, user=self.others[0].id, tok=self.others[0].token)
  243. # The other user sends some messages
  244. self.helper.send_event(room, "m.room.encrypted", {}, tok=self.others[0].token)
  245. # We should get emailed about that message
  246. self._check_for_mail()
  247. def _check_for_mail(self):
  248. """Check that the user receives an email notification"""
  249. # Get the stream ordering before it gets sent
  250. pushers = self.get_success(
  251. self.hs.get_datastore().get_pushers_by({"user_name": self.user_id})
  252. )
  253. pushers = list(pushers)
  254. self.assertEqual(len(pushers), 1)
  255. last_stream_ordering = pushers[0].last_stream_ordering
  256. # Advance time a bit, so the pusher will register something has happened
  257. self.pump(10)
  258. # It hasn't succeeded yet, so the stream ordering shouldn't have moved
  259. pushers = self.get_success(
  260. self.hs.get_datastore().get_pushers_by({"user_name": self.user_id})
  261. )
  262. pushers = list(pushers)
  263. self.assertEqual(len(pushers), 1)
  264. self.assertEqual(last_stream_ordering, pushers[0].last_stream_ordering)
  265. # One email was attempted to be sent
  266. self.assertEqual(len(self.email_attempts), 1)
  267. # Make the email succeed
  268. self.email_attempts[0][0].callback(True)
  269. self.pump()
  270. # One email was attempted to be sent
  271. self.assertEqual(len(self.email_attempts), 1)
  272. # The stream ordering has increased
  273. pushers = self.get_success(
  274. self.hs.get_datastore().get_pushers_by({"user_name": self.user_id})
  275. )
  276. pushers = list(pushers)
  277. self.assertEqual(len(pushers), 1)
  278. self.assertTrue(pushers[0].last_stream_ordering > last_stream_ordering)
  279. # Reset the attempts.
  280. self.email_attempts = []