async def workspace_continue_reencryption(self, workspace_id: EntryID) -> ReencryptionJob: """ Raises: FSError FSBackendOfflineError FSWorkspaceNoAccess FSWorkspaceNotFoundError """ user_manifest = self.get_user_manifest() workspace_entry = user_manifest.get_workspace_entry(workspace_id) if not workspace_entry: raise FSWorkspaceNotFoundError(f"Unknown workspace `{workspace_id}`") # First make sure the workspace is under maintenance try: rep = await self.backend_cmds.realm_status(workspace_entry.id) except BackendNotAvailable as exc: raise FSBackendOfflineError(str(exc)) from exc except BackendConnectionError as exc: raise FSError( f"Cannot continue maintenance on workspace {workspace_id}: {exc}" ) from exc if rep["status"] == "not_allowed": raise FSWorkspaceNoAccess(f"Not allowed to access workspace {workspace_id}: {rep}") elif rep["status"] != "ok": raise FSError(f"Error while getting status for workspace {workspace_id}: {rep}") if not rep["in_maintenance"] or rep["maintenance_type"] != MaintenanceType.REENCRYPTION: raise FSWorkspaceNotInMaintenance("Not in reencryption maintenance") current_encryption_revision = rep["encryption_revision"] if rep["encryption_revision"] != workspace_entry.encryption_revision: raise FSError("Bad encryption revision") # Must retrieve the previous encryption revision's key version_to_fetch = None while True: previous_user_manifest = await self._fetch_remote_user_manifest( version=version_to_fetch ) previous_workspace_entry = previous_user_manifest.get_workspace_entry( workspace_entry.id ) if not previous_workspace_entry: raise FSError( f"Never had access to encryption revision {current_encryption_revision - 1}" ) if previous_workspace_entry.encryption_revision == current_encryption_revision - 1: break else: version_to_fetch = previous_user_manifest.version - 1 return ReencryptionJob(self.backend_cmds, workspace_entry, previous_workspace_entry)
async def _send_start_reencryption_cmd( self, workspace_id: EntryID, encryption_revision: int, timestamp: DateTime, per_user_ciphered_msgs: Dict[UserID, bytes], ) -> bool: """ Raises: FSError FSBackendOfflineError FSWorkspaceNoAccess BackendCmdsParticipantsMismatchError """ # Finally send command to the backend try: rep = await self.backend_cmds.realm_start_reencryption_maintenance( RealmID(workspace_id.uuid), encryption_revision, timestamp, per_user_ciphered_msgs) except BackendNotAvailable as exc: raise FSBackendOfflineError(str(exc)) from exc except BackendConnectionError as exc: raise FSError( f"Cannot start maintenance on workspace {workspace_id}: {exc}" ) from exc if rep["status"] == "participants_mismatch": # Catched by caller return False elif rep["status"] == "in_maintenance": raise FSWorkspaceInMaintenance( f"Workspace {workspace_id} already in maintenance: {rep}") elif rep["status"] == "not_allowed": raise FSWorkspaceNoAccess( f"Not allowed to start maintenance on workspace {workspace_id}: {rep}" ) elif rep["status"] != "ok": raise FSError( f"Cannot start maintenance on workspace {workspace_id}: {rep}") return True
async def do_one_batch(self, size=100) -> Tuple[int, int]: """ Raises: FSError FSBackendOfflineError FSWorkspaceInMaintenance FSWorkspaceNoAccess """ workspace_id = self.new_workspace_entry.id new_encryption_revision = self.new_workspace_entry.encryption_revision # Get the batch try: rep = await self.backend_cmds.vlob_maintenance_get_reencryption_batch( workspace_id, new_encryption_revision, size ) if rep["status"] in ("not_in_maintenance", "bad_encryption_revision"): raise FSWorkspaceNotInMaintenance(f"Reencryption job already finished: {rep}") elif rep["status"] == "not_allowed": raise FSWorkspaceNoAccess( f"Not allowed to do reencryption maintenance on workspace {workspace_id}: {rep}" ) elif rep["status"] != "ok": raise FSError( f"Cannot do reencryption maintenance on workspace {workspace_id}: {rep}" ) donebatch = [] for item in rep["batch"]: cleartext = self.old_workspace_entry.key.decrypt(item["blob"]) newciphered = self.new_workspace_entry.key.encrypt(cleartext) donebatch.append((item["vlob_id"], item["version"], newciphered)) rep = await self.backend_cmds.vlob_maintenance_save_reencryption_batch( workspace_id, new_encryption_revision, donebatch ) if rep["status"] in ("not_in_maintenance", "bad_encryption_revision"): raise FSWorkspaceNotInMaintenance(f"Reencryption job already finished: {rep}") elif rep["status"] == "not_allowed": raise FSWorkspaceNoAccess( f"Not allowed to do reencryption maintenance on workspace {workspace_id}: {rep}" ) elif rep["status"] != "ok": raise FSError( f"Cannot do reencryption maintenance on workspace {workspace_id}: {rep}" ) total = rep["total"] done = rep["done"] if total == done: # Finish the maintenance rep = await self.backend_cmds.realm_finish_reencryption_maintenance( workspace_id, new_encryption_revision ) if rep["status"] in ("not_in_maintenance", "bad_encryption_revision"): raise FSWorkspaceNotInMaintenance(f"Reencryption job already finished: {rep}") elif rep["status"] == "not_allowed": raise FSWorkspaceNoAccess( f"Not allowed to do reencryption maintenance on workspace {workspace_id}: {rep}" ) elif rep["status"] != "ok": raise FSError( f"Cannot do reencryption maintenance on workspace {workspace_id}: {rep}" ) except BackendNotAvailable as exc: raise FSBackendOfflineError(str(exc)) from exc except BackendConnectionError as exc: raise FSError( f"Cannot do reencryption maintenance on workspace {workspace_id}: {exc}" ) from exc return total, done