1
0

test_deactivate_account.py 11 KB

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