diff --git a/synapse/storage/databases/main/e2e_room_keys.py b/synapse/storage/databases/main/e2e_room_keys.py
index 9f8d2e4bea..d01f28cc80 100644
--- a/synapse/storage/databases/main/e2e_room_keys.py
+++ b/synapse/storage/databases/main/e2e_room_keys.py
@@ -13,17 +13,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from typing import Dict, Iterable, Mapping, Optional, Tuple, cast
+from typing import TYPE_CHECKING, Dict, Iterable, Mapping, Optional, Tuple, cast
from typing_extensions import Literal, TypedDict
from synapse.api.errors import StoreError
from synapse.logging.opentracing import log_kv, trace
from synapse.storage._base import SQLBaseStore, db_to_json
-from synapse.storage.database import LoggingTransaction
+from synapse.storage.database import (
+ DatabasePool,
+ LoggingDatabaseConnection,
+ LoggingTransaction,
+)
from synapse.types import JsonDict, JsonSerializable, StreamKeyType
from synapse.util import json_encoder
+if TYPE_CHECKING:
+ from synapse.server import HomeServer
+
class RoomKey(TypedDict):
"""`KeyBackupData` in the Matrix spec.
@@ -37,7 +44,82 @@ class RoomKey(TypedDict):
session_data: JsonSerializable
-class EndToEndRoomKeyStore(SQLBaseStore):
+class EndToEndRoomKeyBackgroundStore(SQLBaseStore):
+ def __init__(
+ self,
+ database: DatabasePool,
+ db_conn: LoggingDatabaseConnection,
+ hs: "HomeServer",
+ ):
+ super().__init__(database, db_conn, hs)
+
+ self.db_pool.updates.register_background_update_handler(
+ "delete_e2e_backup_keys_for_deactivated_users",
+ self._delete_e2e_backup_keys_for_deactivated_users,
+ )
+
+ def _delete_keys_txn(self, txn: LoggingTransaction, user_id: str) -> None:
+ self.db_pool.simple_delete_txn(
+ txn,
+ table="e2e_room_keys",
+ keyvalues={"user_id": user_id},
+ )
+
+ self.db_pool.simple_delete_txn(
+ txn,
+ table="e2e_room_keys_versions",
+ keyvalues={"user_id": user_id},
+ )
+
+ async def _delete_e2e_backup_keys_for_deactivated_users(
+ self, progress: JsonDict, batch_size: int
+ ) -> int:
+ """
+ Retroactively purges account data for users that have already been deactivated.
+ Gets run as a background update caused by a schema delta.
+ """
+
+ last_user: str = progress.get("last_user", "")
+
+ def _delete_backup_keys_for_deactivated_users_txn(
+ txn: LoggingTransaction,
+ ) -> int:
+ sql = """
+ SELECT name FROM users
+ WHERE deactivated = ? and name > ?
+ ORDER BY name ASC
+ LIMIT ?
+ """
+
+ txn.execute(sql, (1, last_user, batch_size))
+ users = [row[0] for row in txn]
+
+ for user in users:
+ self._delete_keys_txn(txn, user)
+
+ if users:
+ self.db_pool.updates._background_update_progress_txn(
+ txn,
+ "delete_e2e_backup_keys_for_deactivated_users",
+ {"last_user": users[-1]},
+ )
+
+ return len(users)
+
+ number_deleted = await self.db_pool.runInteraction(
+ "_delete_backup_keys_for_deactivated_users",
+ _delete_backup_keys_for_deactivated_users_txn,
+ )
+
+ if number_deleted < batch_size:
+ await self.db_pool.updates._end_background_update(
+ "delete_e2e_backup_keys_for_deactivated_users"
+ )
+
+ return number_deleted
+
+
+class EndToEndRoomKeyStore(EndToEndRoomKeyBackgroundStore):
"""The store for end to end room key backups.
See https://spec.matrix.org/v1.1/client-server-api/#server-side-key-backups
@@ -550,3 +632,29 @@ class EndToEndRoomKeyStore(SQLBaseStore):
await self.db_pool.runInteraction(
"delete_e2e_room_keys_version", _delete_e2e_room_keys_version_txn
)
+
+ async def bulk_delete_backup_keys_and_versions_for_user(self, user_id: str) -> None:
+ """
+ Bulk deletes all backup room keys and versions for a given user.
+
+ Args:
+ user_id: the user whose backup keys and versions we're deleting
+ """
+
+ def _delete_all_e2e_room_keys_and_versions_txn(txn: LoggingTransaction) -> None:
+ self.db_pool.simple_delete_txn(
+ txn,
+ table="e2e_room_keys",
+ keyvalues={"user_id": user_id},
+ )
+
+ self.db_pool.simple_delete_txn(
+ txn,
+ table="e2e_room_keys_versions",
+ keyvalues={"user_id": user_id},
+ )
+
+ await self.db_pool.runInteraction(
+ "delete_all_e2e_room_keys_and_versions",
+ _delete_all_e2e_room_keys_and_versions_txn,
+ )
diff --git a/synapse/storage/schema/main/delta/74/04_delete_e2e_backup_keys_for_deactivated_users.sql b/synapse/storage/schema/main/delta/74/04_delete_e2e_backup_keys_for_deactivated_users.sql
new file mode 100644
index 0000000000..a194f4cece
--- /dev/null
+++ b/synapse/storage/schema/main/delta/74/04_delete_e2e_backup_keys_for_deactivated_users.sql
@@ -0,0 +1,17 @@
+/* Copyright 2023 The Matrix.org Foundation C.I.C
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+INSERT INTO background_updates (ordering, update_name, progress_json) VALUES
+ (7404, 'delete_e2e_backup_keys_for_deactivated_users', '{}');
\ No newline at end of file
|