Quellcode durchsuchen

fix: archive download — remplacer sudo cat par sudo rsync vers /tmp (cat non autorisé sudoers)

Cédric Hansen vor 21 Stunden
Ursprung
Commit
c88663fbbc
1 geänderte Dateien mit 33 neuen und 11 gelöschten Zeilen
  1. 33 11
      sources/app.py

+ 33 - 11
sources/app.py

@@ -787,23 +787,45 @@ def api_upload_finish(upload_id):
 
 @app.route("/api/v1/archives/<name>/download")
 def api_archive_download(name):
-    """Téléchargement direct d'une archive via sudo cat (pour pull inter-instances)."""
+    """Téléchargement d'une archive via sudo rsync vers /tmp (pour pull inter-instances)."""
+    from flask import Response, stream_with_context
+    from jobs.utils import sudo_exists
+
     backup_dir = app.config["YUNOHOST_BACKUP_DIR"]
     archive_path = os.path.join(backup_dir, name + ".tar")
-    from jobs.utils import sudo_exists
     if not sudo_exists(archive_path):
         return jsonify({"error": "archive introuvable"}), 404
 
-    result = subprocess.run(["sudo", "cat", archive_path], capture_output=True, timeout=3600)
-    if result.returncode != 0:
-        return jsonify({"error": "lecture échouée"}), 500
+    tmp_path = f"/tmp/backupmanager_dl_{name}.tar"
+    try:
+        result = subprocess.run(
+            ["sudo", "rsync", archive_path, tmp_path],
+            capture_output=True, text=True, timeout=3600,
+        )
+        if result.returncode != 0:
+            return jsonify({"error": result.stderr.strip()}), 500
 
-    from flask import Response
-    return Response(
-        result.stdout,
-        mimetype="application/octet-stream",
-        headers={"Content-Disposition": f'attachment; filename="{name}.tar"'},
-    )
+        def stream_and_cleanup():
+            try:
+                with open(tmp_path, "rb") as f:
+                    while True:
+                        chunk = f.read(1024 * 1024)
+                        if not chunk:
+                            break
+                        yield chunk
+            finally:
+                if os.path.exists(tmp_path):
+                    os.unlink(tmp_path)
+
+        return Response(
+            stream_with_context(stream_and_cleanup()),
+            mimetype="application/octet-stream",
+            headers={"Content-Disposition": f'attachment; filename="{name}.tar"'},
+        )
+    except Exception as exc:
+        if os.path.exists(tmp_path):
+            os.unlink(tmp_path)
+        return jsonify({"error": str(exc)}), 500
 
 
 @app.route("/api/v1/archives/upload/<upload_id>", methods=["DELETE"])