Refactor the core backup and encryption logic to use a new, consistent directory structure. This new structure separates encrypted and unencrypted backups and centralizes metadata, making the system more robust and easier to manage.
Key changes:
- Implemented a new directory scheme:
/pybackup/
├── unencrypted/{system,user}/<source>/
├── encrypted/{system,user}/<source>/ (mount point)
├── metadata/
└── pybackup_encrypted.luks
- Reworked path generation logic in BackupManager and EncryptionManager to support the new structure.
- All backup, restore, and listing operations now correctly resolve paths based on the new scheme.
This also includes several bug fixes identified during the refactoring:
- fix(backup): Correctly quote rsync paths for user backups to prevent "No such file or directory" errors.
- fix(encryption): Change key lookup order to Keyring -> Keyfile -> Password Prompt, as requested.
- fix(ui): Remove eager auto-mount on startup to prevent unexpected password prompts. The app now only mounts when required by a user action.
120 lines
4.6 KiB
Python
120 lines
4.6 KiB
Python
import tkinter as tk
|
|
import os
|
|
|
|
from core.pbp_app_config import Msg
|
|
from shared_libs.common_tools import IconManager
|
|
from shared_libs.logger import app_logger
|
|
|
|
class HeaderFrame(tk.Frame):
|
|
def __init__(self, container, image_manager, encryption_manager, app, **kwargs):
|
|
super().__init__(container, bg="#455A64", **kwargs)
|
|
|
|
self.image_manager = image_manager
|
|
self.encryption_manager = encryption_manager
|
|
self.app = app
|
|
|
|
# Configure grid weights for internal layout
|
|
self.columnconfigure(1, weight=1) # Make the middle column expand
|
|
self.rowconfigure(0, weight=1) # Make the top row expand
|
|
|
|
# Left side: Icon and Main Title/Subtitle
|
|
left_frame = tk.Frame(self, bg="#455A64")
|
|
left_frame.grid(row=0, column=0, rowspan=2, sticky="nsew")
|
|
left_frame.columnconfigure(0, weight=1)
|
|
left_frame.rowconfigure(0, weight=1)
|
|
|
|
icon_label = tk.Label(
|
|
left_frame,
|
|
image=self.image_manager.get_icon(
|
|
"backup_extralarge"), # Using a generic backup icon
|
|
bg="#455A64",
|
|
)
|
|
icon_label.grid(row=0, column=0, sticky="e", padx=10, pady=5)
|
|
|
|
title_label = tk.Label(
|
|
self,
|
|
text=Msg.STR["header_title"],
|
|
font=("Helvetica", 16, "bold"),
|
|
fg="#ffffff",
|
|
bg="#455A64",
|
|
)
|
|
title_label.grid(row=0, column=1, sticky="w",
|
|
padx=(5, 20), pady=(15, 5))
|
|
|
|
subtitle_label = tk.Label(
|
|
self,
|
|
text=Msg.STR["header_subtitle"],
|
|
font=("Helvetica", 10),
|
|
fg="#bdc3c7",
|
|
bg="#455A64",
|
|
)
|
|
subtitle_label.grid(row=1, column=1, sticky="w",
|
|
padx=(5, 20), pady=(0, 10))
|
|
|
|
# Right side: Keyring status
|
|
right_frame = tk.Frame(self, bg="#455A64")
|
|
right_frame.grid(row=0, column=2, rowspan=2, sticky="nsew")
|
|
right_frame.columnconfigure(0, weight=1)
|
|
right_frame.rowconfigure(0, weight=1)
|
|
|
|
self.keyring_status_label = tk.Label(
|
|
right_frame,
|
|
text="",
|
|
font=("Helvetica", 10, "bold"),
|
|
bg="#455A64",
|
|
)
|
|
self.keyring_status_label.grid(row=0, column=0, sticky="ne", padx=(10, 10), pady=(10, 0))
|
|
|
|
self.refresh_status()
|
|
|
|
def refresh_status(self):
|
|
"""Checks the keyring status based on the current destination and updates the label."""
|
|
app_logger.log("HeaderFrame: Refreshing status...")
|
|
dest_path = self.app.destination_path
|
|
app_logger.log(f"HeaderFrame: Destination path is '{dest_path}'")
|
|
|
|
if not dest_path or not self.encryption_manager.is_encrypted(dest_path):
|
|
app_logger.log("HeaderFrame: No destination path or not encrypted. Clearing status.")
|
|
self.keyring_status_label.config(text="") # Clear status if not encrypted
|
|
return
|
|
|
|
app_logger.log("HeaderFrame: Destination is encrypted.")
|
|
username = os.path.basename(dest_path.rstrip('/'))
|
|
app_logger.log(f"HeaderFrame: Username is '{username}'")
|
|
|
|
is_mounted = self.encryption_manager.is_mounted(dest_path)
|
|
app_logger.log(f"HeaderFrame: Is mounted? {is_mounted}")
|
|
|
|
if is_mounted:
|
|
status_text = "Key: In Use"
|
|
auth_method = getattr(self.encryption_manager, 'auth_method', None)
|
|
if auth_method == 'keyring':
|
|
status_text += " (Keyring)"
|
|
elif auth_method == 'keyfile':
|
|
status_text += " (Keyfile)"
|
|
self.keyring_status_label.config(
|
|
text=status_text,
|
|
fg="#2E8B57" # SeaGreen
|
|
)
|
|
else:
|
|
key_in_keyring = self.encryption_manager.is_key_in_keyring(username)
|
|
app_logger.log(f"HeaderFrame: Key in keyring? {key_in_keyring}")
|
|
key_file_exists = os.path.exists(self.encryption_manager.get_key_file_path(dest_path))
|
|
app_logger.log(f"HeaderFrame: Key file exists? {key_file_exists}")
|
|
|
|
if key_in_keyring:
|
|
self.keyring_status_label.config(
|
|
text="Key: Available (Keyring)",
|
|
fg="#FFD700" # Gold
|
|
)
|
|
elif key_file_exists:
|
|
self.keyring_status_label.config(
|
|
text="Key: Available (Keyfile)",
|
|
fg="#FFD700" # Gold
|
|
)
|
|
else:
|
|
self.keyring_status_label.config(
|
|
text="Key: Not Available",
|
|
fg="#A9A9A9" # DarkGray
|
|
)
|
|
app_logger.log("HeaderFrame: Status refresh complete.") |