user_erasure_store.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2018 New Vector Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import operator
  16. from twisted.internet import defer
  17. from synapse.storage._base import SQLBaseStore
  18. from synapse.util.caches.descriptors import cachedList, cached
  19. class UserErasureWorkerStore(SQLBaseStore):
  20. @cached()
  21. def is_user_erased(self, user_id):
  22. """
  23. Check if the given user id has requested erasure
  24. Args:
  25. user_id (str): full user id to check
  26. Returns:
  27. Deferred[bool]: True if the user has requested erasure
  28. """
  29. return self._simple_select_onecol(
  30. table="erased_users",
  31. keyvalues={"user_id": user_id},
  32. retcol="1",
  33. desc="is_user_erased",
  34. ).addCallback(operator.truth)
  35. @cachedList(
  36. cached_method_name="is_user_erased",
  37. list_name="user_ids",
  38. inlineCallbacks=True,
  39. )
  40. def are_users_erased(self, user_ids):
  41. """
  42. Checks which users in a list have requested erasure
  43. Args:
  44. user_ids (iterable[str]): full user id to check
  45. Returns:
  46. Deferred[dict[str, bool]]:
  47. for each user, whether the user has requested erasure.
  48. """
  49. # this serves the dual purpose of (a) making sure we can do len and
  50. # iterate it multiple times, and (b) avoiding duplicates.
  51. user_ids = tuple(set(user_ids))
  52. def _get_erased_users(txn):
  53. txn.execute(
  54. "SELECT user_id FROM erased_users WHERE user_id IN (%s)" % (
  55. ",".join("?" * len(user_ids))
  56. ),
  57. user_ids,
  58. )
  59. return set(r[0] for r in txn)
  60. erased_users = yield self.runInteraction(
  61. "are_users_erased", _get_erased_users,
  62. )
  63. res = dict((u, u in erased_users) for u in user_ids)
  64. defer.returnValue(res)
  65. class UserErasureStore(UserErasureWorkerStore):
  66. def mark_user_erased(self, user_id):
  67. """Indicate that user_id wishes their message history to be erased.
  68. Args:
  69. user_id (str): full user_id to be erased
  70. """
  71. def f(txn):
  72. # first check if they are already in the list
  73. txn.execute(
  74. "SELECT 1 FROM erased_users WHERE user_id = ?",
  75. (user_id, )
  76. )
  77. if txn.fetchone():
  78. return
  79. # they are not already there: do the insert.
  80. txn.execute(
  81. "INSERT INTO erased_users (user_id) VALUES (?)",
  82. (user_id, )
  83. )
  84. self._invalidate_cache_and_stream(
  85. txn, self.is_user_erased, (user_id,)
  86. )
  87. return self.runInteraction("mark_user_erased", f)