mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
Merge pull request #702 from PR0M3TH3AN/codex/edit-manager.py-to-handle-import-errors
Improve database import validation
This commit is contained in:
@@ -32,6 +32,7 @@ from .password_generation import PasswordGenerator
|
|||||||
from .backup import BackupManager
|
from .backup import BackupManager
|
||||||
from .vault import Vault
|
from .vault import Vault
|
||||||
from .portable_backup import export_backup, import_backup
|
from .portable_backup import export_backup, import_backup
|
||||||
|
from cryptography.fernet import InvalidToken
|
||||||
from .totp import TotpManager
|
from .totp import TotpManager
|
||||||
from .entry_types import EntryType
|
from .entry_types import EntryType
|
||||||
from .pubsub import bus
|
from .pubsub import bus
|
||||||
@@ -4013,26 +4014,57 @@ class PasswordManager:
|
|||||||
|
|
||||||
def handle_import_database(self, src: Path) -> None:
|
def handle_import_database(self, src: Path) -> None:
|
||||||
"""Import a portable database file, replacing the current index."""
|
"""Import a portable database file, replacing the current index."""
|
||||||
try:
|
|
||||||
fp, parent_fp, child_fp = self.header_fingerprint_args
|
if not src.name.endswith(".json.enc"):
|
||||||
clear_header_with_notification(
|
print(
|
||||||
self,
|
colored(
|
||||||
fp,
|
"Error: Selected file must be a SeedPass database backup (.json.enc).",
|
||||||
"Main Menu > Settings > Import database",
|
"red",
|
||||||
parent_fingerprint=parent_fp,
|
)
|
||||||
child_fingerprint=child_fp,
|
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
fp, parent_fp, child_fp = self.header_fingerprint_args
|
||||||
|
clear_header_with_notification(
|
||||||
|
self,
|
||||||
|
fp,
|
||||||
|
"Main Menu > Settings > Import database",
|
||||||
|
parent_fingerprint=parent_fp,
|
||||||
|
child_fingerprint=child_fp,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
import_backup(
|
import_backup(
|
||||||
self.vault,
|
self.vault,
|
||||||
self.backup_manager,
|
self.backup_manager,
|
||||||
src,
|
src,
|
||||||
parent_seed=self.parent_seed,
|
parent_seed=self.parent_seed,
|
||||||
)
|
)
|
||||||
print(colored("Database imported successfully.", "green"))
|
except InvalidToken:
|
||||||
self.sync_vault()
|
logging.error("Invalid backup token during import", exc_info=True)
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
"Error: Invalid backup. Please import a file created by SeedPass.",
|
||||||
|
"red",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
except FileNotFoundError:
|
||||||
|
logging.error(f"Backup file not found: {src}", exc_info=True)
|
||||||
|
print(colored(f"Error: File '{src}' not found.", "red"))
|
||||||
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to import database: {e}", exc_info=True)
|
logging.error(f"Failed to import database: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to import database: {e}", "red"))
|
print(
|
||||||
|
colored(
|
||||||
|
f"Error: Failed to import database: {e}. Please verify the backup file.",
|
||||||
|
"red",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(colored("Database imported successfully.", "green"))
|
||||||
|
self.sync_vault()
|
||||||
|
|
||||||
def handle_export_totp_codes(self) -> Path | None:
|
def handle_export_totp_codes(self) -> Path | None:
|
||||||
"""Export all 2FA codes to a JSON file for other authenticator apps."""
|
"""Export all 2FA codes to a JSON file for other authenticator apps."""
|
||||||
|
55
src/tests/test_manager_import_database.py
Normal file
55
src/tests/test_manager_import_database.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import queue
|
||||||
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||||
|
|
||||||
|
from seedpass.core.manager import PasswordManager, EncryptionMode
|
||||||
|
|
||||||
|
|
||||||
|
def _make_pm() -> PasswordManager:
|
||||||
|
pm = PasswordManager.__new__(PasswordManager)
|
||||||
|
pm.encryption_mode = EncryptionMode.SEED_ONLY
|
||||||
|
pm.vault = SimpleNamespace()
|
||||||
|
pm.backup_manager = SimpleNamespace()
|
||||||
|
pm.parent_seed = "seed"
|
||||||
|
pm.profile_stack = []
|
||||||
|
pm.current_fingerprint = None
|
||||||
|
pm.sync_vault = lambda: None
|
||||||
|
pm.notifications = queue.Queue()
|
||||||
|
return pm
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_non_backup_file(monkeypatch, capsys):
|
||||||
|
pm = _make_pm()
|
||||||
|
called = {"called": False}
|
||||||
|
|
||||||
|
def fake_import(*_a, **_k):
|
||||||
|
called["called"] = True
|
||||||
|
|
||||||
|
monkeypatch.setattr("seedpass.core.manager.import_backup", fake_import)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"seedpass.core.manager.clear_header_with_notification", lambda *a, **k: None
|
||||||
|
)
|
||||||
|
|
||||||
|
pm.handle_import_database(Path("data.txt"))
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "json.enc" in out.lower()
|
||||||
|
assert called["called"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_missing_file(monkeypatch, capsys):
|
||||||
|
pm = _make_pm()
|
||||||
|
|
||||||
|
def raise_missing(*_a, **_k):
|
||||||
|
raise FileNotFoundError
|
||||||
|
|
||||||
|
monkeypatch.setattr("seedpass.core.manager.import_backup", raise_missing)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"seedpass.core.manager.clear_header_with_notification", lambda *a, **k: None
|
||||||
|
)
|
||||||
|
|
||||||
|
pm.handle_import_database(Path("missing.json.enc"))
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "not found" in out.lower()
|
Reference in New Issue
Block a user