test_deactivate_account.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. # Copyright 2021 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. from http import HTTPStatus
  15. from typing import Any, Dict
  16. from twisted.test.proto_helpers import MemoryReactor
  17. from synapse.api.constants import AccountDataTypes
  18. from synapse.push.rulekinds import PRIORITY_CLASS_MAP
  19. from synapse.rest import admin
  20. from synapse.rest.client import account, login
  21. from synapse.server import HomeServer
  22. from synapse.util import Clock
  23. from tests.unittest import HomeserverTestCase
  24. class DeactivateAccountTestCase(HomeserverTestCase):
  25. servlets = [
  26. login.register_servlets,
  27. admin.register_servlets,
  28. account.register_servlets,
  29. ]
  30. def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
  31. self._store = hs.get_datastores().main
  32. self.user = self.register_user("user", "pass")
  33. self.token = self.login("user", "pass")
  34. def _deactivate_my_account(self) -> None:
  35. """
  36. Deactivates the account `self.user` using `self.token` and asserts
  37. that it returns a 200 success code.
  38. """
  39. req = self.get_success(
  40. self.make_request(
  41. "POST",
  42. "account/deactivate",
  43. {
  44. "auth": {
  45. "type": "m.login.password",
  46. "user": self.user,
  47. "password": "pass",
  48. },
  49. "erase": True,
  50. },
  51. access_token=self.token,
  52. )
  53. )
  54. self.assertEqual(req.code, HTTPStatus.OK, req)
  55. def test_global_account_data_deleted_upon_deactivation(self) -> None:
  56. """
  57. Tests that global account data is removed upon deactivation.
  58. """
  59. # Add some account data
  60. self.get_success(
  61. self._store.add_account_data_for_user(
  62. self.user,
  63. AccountDataTypes.DIRECT,
  64. {"@someone:remote": ["!somewhere:remote"]},
  65. )
  66. )
  67. # Check that we actually added some.
  68. self.assertIsNotNone(
  69. self.get_success(
  70. self._store.get_global_account_data_by_type_for_user(
  71. self.user, AccountDataTypes.DIRECT
  72. )
  73. ),
  74. )
  75. # Request the deactivation of our account
  76. self._deactivate_my_account()
  77. # Check that the account data does not persist.
  78. self.assertIsNone(
  79. self.get_success(
  80. self._store.get_global_account_data_by_type_for_user(
  81. self.user, AccountDataTypes.DIRECT
  82. )
  83. ),
  84. )
  85. def test_room_account_data_deleted_upon_deactivation(self) -> None:
  86. """
  87. Tests that room account data is removed upon deactivation.
  88. """
  89. room_id = "!room:test"
  90. # Add some room account data
  91. self.get_success(
  92. self._store.add_account_data_to_room(
  93. self.user,
  94. room_id,
  95. "m.fully_read",
  96. {"event_id": "$aaaa:test"},
  97. )
  98. )
  99. # Check that we actually added some.
  100. self.assertIsNotNone(
  101. self.get_success(
  102. self._store.get_account_data_for_room_and_type(
  103. self.user, room_id, "m.fully_read"
  104. )
  105. ),
  106. )
  107. # Request the deactivation of our account
  108. self._deactivate_my_account()
  109. # Check that the account data does not persist.
  110. self.assertIsNone(
  111. self.get_success(
  112. self._store.get_account_data_for_room_and_type(
  113. self.user, room_id, "m.fully_read"
  114. )
  115. ),
  116. )
  117. def _is_custom_rule(self, push_rule: Dict[str, Any]) -> bool:
  118. """
  119. Default rules start with a dot: such as .m.rule and .im.vector.
  120. This function returns true iff a rule is custom (not default).
  121. """
  122. return "/." not in push_rule["rule_id"]
  123. def test_push_rules_deleted_upon_account_deactivation(self) -> None:
  124. """
  125. Push rules are a special case of account data.
  126. They are stored separately but get sent to the client as account data in /sync.
  127. This tests that deactivating a user deletes push rules along with the rest
  128. of their account data.
  129. """
  130. # Add a push rule
  131. self.get_success(
  132. self._store.add_push_rule(
  133. self.user,
  134. "personal.override.rule1",
  135. PRIORITY_CLASS_MAP["override"],
  136. [],
  137. [],
  138. )
  139. )
  140. # Test the rule exists
  141. push_rules = self.get_success(self._store.get_push_rules_for_user(self.user))
  142. # Filter out default rules; we don't care
  143. push_rules = list(filter(self._is_custom_rule, push_rules))
  144. # Check our rule made it
  145. self.assertEqual(
  146. push_rules,
  147. [
  148. {
  149. "user_name": "@user:test",
  150. "rule_id": "personal.override.rule1",
  151. "priority_class": 5,
  152. "priority": 0,
  153. "conditions": [],
  154. "actions": [],
  155. "default": False,
  156. }
  157. ],
  158. push_rules,
  159. )
  160. # Request the deactivation of our account
  161. self._deactivate_my_account()
  162. push_rules = self.get_success(self._store.get_push_rules_for_user(self.user))
  163. # Filter out default rules; we don't care
  164. push_rules = list(filter(self._is_custom_rule, push_rules))
  165. # Check our rule no longer exists
  166. self.assertEqual(push_rules, [], push_rules)
  167. def test_ignored_users_deleted_upon_deactivation(self) -> None:
  168. """
  169. Ignored users are a special case of account data.
  170. They get denormalised into the `ignored_users` table upon being stored as
  171. account data.
  172. Test that a user's list of ignored users is deleted upon deactivation.
  173. """
  174. # Add an ignored user
  175. self.get_success(
  176. self._store.add_account_data_for_user(
  177. self.user,
  178. AccountDataTypes.IGNORED_USER_LIST,
  179. {"ignored_users": {"@sheltie:test": {}}},
  180. )
  181. )
  182. # Test the user is ignored
  183. self.assertEqual(
  184. self.get_success(self._store.ignored_by("@sheltie:test")), {self.user}
  185. )
  186. # Request the deactivation of our account
  187. self._deactivate_my_account()
  188. # Test the user is no longer ignored by the user that was deactivated
  189. self.assertEqual(
  190. self.get_success(self._store.ignored_by("@sheltie:test")), set()
  191. )
  192. def _rerun_retroactive_account_data_deletion_update(self) -> None:
  193. # Reset the 'all done' flag
  194. self._store.db_pool.updates._all_done = False
  195. self.get_success(
  196. self._store.db_pool.simple_insert(
  197. "background_updates",
  198. {
  199. "update_name": "delete_account_data_for_deactivated_users",
  200. "progress_json": "{}",
  201. },
  202. )
  203. )
  204. self.wait_for_background_updates()
  205. def test_account_data_deleted_retroactively_by_background_update_if_deactivated(
  206. self,
  207. ) -> None:
  208. """
  209. Tests that a user, who deactivated their account before account data was
  210. deleted automatically upon deactivation, has their account data retroactively
  211. scrubbed by the background update.
  212. """
  213. # Request the deactivation of our account
  214. self._deactivate_my_account()
  215. # Add some account data
  216. # (we do this after the deactivation so that the act of deactivating doesn't
  217. # clear it out. This emulates a user that was deactivated before this was cleared
  218. # upon deactivation.)
  219. self.get_success(
  220. self._store.add_account_data_for_user(
  221. self.user,
  222. AccountDataTypes.DIRECT,
  223. {"@someone:remote": ["!somewhere:remote"]},
  224. )
  225. )
  226. # Check that the account data is there.
  227. self.assertIsNotNone(
  228. self.get_success(
  229. self._store.get_global_account_data_by_type_for_user(
  230. self.user,
  231. AccountDataTypes.DIRECT,
  232. )
  233. ),
  234. )
  235. # Re-run the retroactive deletion update
  236. self._rerun_retroactive_account_data_deletion_update()
  237. # Check that the account data was cleared.
  238. self.assertIsNone(
  239. self.get_success(
  240. self._store.get_global_account_data_by_type_for_user(
  241. self.user,
  242. AccountDataTypes.DIRECT,
  243. )
  244. ),
  245. )
  246. def test_account_data_preserved_by_background_update_if_not_deactivated(
  247. self,
  248. ) -> None:
  249. """
  250. Tests that the background update does not scrub account data for users that have
  251. not been deactivated.
  252. """
  253. # Add some account data
  254. # (we do this after the deactivation so that the act of deactivating doesn't
  255. # clear it out. This emulates a user that was deactivated before this was cleared
  256. # upon deactivation.)
  257. self.get_success(
  258. self._store.add_account_data_for_user(
  259. self.user,
  260. AccountDataTypes.DIRECT,
  261. {"@someone:remote": ["!somewhere:remote"]},
  262. )
  263. )
  264. # Check that the account data is there.
  265. self.assertIsNotNone(
  266. self.get_success(
  267. self._store.get_global_account_data_by_type_for_user(
  268. self.user,
  269. AccountDataTypes.DIRECT,
  270. )
  271. ),
  272. )
  273. # Re-run the retroactive deletion update
  274. self._rerun_retroactive_account_data_deletion_update()
  275. # Check that the account data was NOT cleared.
  276. self.assertIsNotNone(
  277. self.get_success(
  278. self._store.get_global_account_data_by_type_for_user(
  279. self.user,
  280. AccountDataTypes.DIRECT,
  281. )
  282. ),
  283. )