diff --git a/plinth/modules/backups/decorators.py b/plinth/modules/backups/decorators.py index fd500457e..90a34920c 100644 --- a/plinth/modules/backups/decorators.py +++ b/plinth/modules/backups/decorators.py @@ -4,9 +4,8 @@ Decorators for the backup views. """ import functools -import os -from . import SESSION_PATH_VARIABLE +from . import privileged, SESSION_PATH_VARIABLE def delete_tmp_backup_file(function): @@ -15,12 +14,12 @@ def delete_tmp_backup_file(function): XXX: Implement a better way to delete uploaded files. """ + @functools.wraps(function) def wrapper(request, *args, **kwargs): path = request.session.get(SESSION_PATH_VARIABLE, None) if path: - if os.path.isfile(path): - os.remove(path) + privileged.remove_uploaded_archive(path) del request.session[SESSION_PATH_VARIABLE] return function(request, *args, **kwargs) diff --git a/plinth/modules/backups/privileged.py b/plinth/modules/backups/privileged.py index aa2f3298e..be0f2558a 100644 --- a/plinth/modules/backups/privileged.py +++ b/plinth/modules/backups/privileged.py @@ -8,11 +8,13 @@ import re import subprocess import tarfile +from plinth import action_utils from plinth.actions import privileged, secret_str from plinth.utils import Version TIMEOUT = 30 BACKUPS_DATA_PATH = pathlib.Path('/var/lib/plinth/backups-data/') +BACKUPS_UPLOAD_PATH = pathlib.Path('/var/lib/freedombox/backups-upload/') MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/' @@ -143,6 +145,24 @@ def list_repo(path: str, return json.loads(process.stdout.decode()) +@privileged +def add_uploaded_archive(file_name: str, temporary_file_path: str): + """Store an archive uploaded by the user.""" + BACKUPS_UPLOAD_PATH.mkdir(parents=True, exist_ok=True) + action_utils.move_uploaded_file(temporary_file_path, BACKUPS_UPLOAD_PATH, + file_name, allow_overwrite=True, + permissions=0o600) + + +@privileged +def remove_uploaded_archive(file_path: str): + """Delete the archive uploaded by the user.""" + resolved_file_path = pathlib.Path(file_path).resolve() + if (resolved_file_path.is_relative_to(BACKUPS_UPLOAD_PATH) + and resolved_file_path.is_file()): + resolved_file_path.unlink() + + def _get_borg_version(): """Return the version of borgbackup.""" process = _run(['borg', '--version'], stdout=subprocess.PIPE) diff --git a/plinth/modules/backups/views.py b/plinth/modules/backups/views.py index 2dc0e7407..ba4780ffb 100644 --- a/plinth/modules/backups/views.py +++ b/plinth/modules/backups/views.py @@ -5,7 +5,6 @@ Views for the backups app. import logging import os -import tempfile from datetime import datetime from urllib.parse import unquote @@ -190,11 +189,13 @@ class UploadArchiveView(SuccessMessageMixin, FormView): return context def form_valid(self, form): - """store uploaded file.""" - with tempfile.NamedTemporaryFile(delete=False) as tmp_file: - self.request.session[SESSION_PATH_VARIABLE] = tmp_file.name - for chunk in self.request.FILES['backups-file'].chunks(): - tmp_file.write(chunk) + """Store uploaded file.""" + uploaded_file = self.request.FILES['backups-file'] + # Hold on to Django's uploaded file. It will be used by other views. + privileged.add_uploaded_archive(uploaded_file.name, + uploaded_file.temporary_file_path()) + self.request.session[SESSION_PATH_VARIABLE] = str( + privileged.BACKUPS_UPLOAD_PATH / uploaded_file.name) return super().form_valid(form)