Merge pull request #701 from PR0M3TH3AN/codex/verify-and-improve-manifest-identifier-handling

Fix manifest ID handling and add restore test
This commit is contained in:
thePR0M3TH3AN
2025-07-31 14:49:52 -04:00
committed by GitHub
3 changed files with 76 additions and 6 deletions

View File

@@ -406,6 +406,7 @@ def handle_retrieve_from_nostr(password_manager: PasswordManager):
Handles the action of retrieving the encrypted password index from Nostr.
"""
try:
password_manager.nostr_client.fingerprint = password_manager.current_fingerprint
result = asyncio.run(password_manager.nostr_client.fetch_latest_snapshot())
if result:
manifest, chunks = result
@@ -423,8 +424,12 @@ def handle_retrieve_from_nostr(password_manager: PasswordManager):
print(colored("Encrypted index retrieved and saved successfully.", "green"))
logging.info("Encrypted index retrieved and saved successfully from Nostr.")
else:
print(colored("Failed to retrieve data from Nostr.", "red"))
logging.error("Failed to retrieve data from Nostr.")
msg = (
f"No Nostr events found for fingerprint"
f" {password_manager.current_fingerprint}."
)
print(colored(msg, "red"))
logging.error(msg)
except Exception as e:
logging.error(f"Failed to retrieve from Nostr: {e}", exc_info=True)
print(colored(f"Error: Failed to retrieve from Nostr: {e}", "red"))

View File

@@ -95,7 +95,7 @@ from datetime import datetime
from utils.fingerprint_manager import FingerprintManager
# Import NostrClient
from nostr.client import NostrClient, DEFAULT_RELAYS
from nostr.client import NostrClient, DEFAULT_RELAYS, MANIFEST_ID_PREFIX
from .config_manager import ConfigManager
from .state_manager import StateManager
@@ -272,6 +272,8 @@ class PasswordManager:
def notify(self, message: str, level: str = "INFO") -> None:
"""Enqueue a notification and set it as the active message."""
note = Notification(message, level)
if not hasattr(self, "notifications"):
self.notifications = queue.Queue()
self.notifications.put(note)
self._current_notification = note
self._notification_expiry = time.time() + NOTIFICATION_DURATION
@@ -605,6 +607,8 @@ class PasswordManager:
selected_fingerprint = fingerprints[int(choice) - 1]
self.fingerprint_manager.current_fingerprint = selected_fingerprint
self.current_fingerprint = selected_fingerprint
if not getattr(self, "manifest_id", None):
self.manifest_id = f"{MANIFEST_ID_PREFIX}{selected_fingerprint}"
# Update fingerprint directory
self.fingerprint_dir = (
@@ -645,7 +649,9 @@ class PasswordManager:
config_manager=getattr(self, "config_manager", None),
parent_seed=getattr(self, "parent_seed", None),
)
if getattr(self, "manifest_id", None):
if getattr(self, "manifest_id", None) and hasattr(
self.nostr_client, "_state_lock"
):
from nostr.backup_models import Manifest
with self.nostr_client._state_lock:
@@ -903,6 +909,8 @@ class PasswordManager:
self.current_fingerprint = fingerprint
self.fingerprint_manager.current_fingerprint = fingerprint
self.fingerprint_dir = fingerprint_dir
if not getattr(self, "manifest_id", None):
self.manifest_id = f"{MANIFEST_ID_PREFIX}{fingerprint}"
logging.info(f"Current seed profile set to {fingerprint}")
try:
@@ -1174,7 +1182,9 @@ class PasswordManager:
parent_seed=getattr(self, "parent_seed", None),
)
if getattr(self, "manifest_id", None):
if getattr(self, "manifest_id", None) and hasattr(
self.nostr_client, "_state_lock"
):
from nostr.backup_models import Manifest
with self.nostr_client._state_lock:
@@ -1197,6 +1207,8 @@ class PasswordManager:
"""Always fetch the latest vault data from Nostr and update the local index."""
start = time.perf_counter()
try:
if getattr(self, "current_fingerprint", None):
self.nostr_client.fingerprint = self.current_fingerprint
result = await self.nostr_client.fetch_latest_snapshot()
if not result:
if self.nostr_client.last_error:
@@ -1346,6 +1358,8 @@ class PasswordManager:
have_data = False
start = time.perf_counter()
try:
if getattr(self, "current_fingerprint", None):
self.nostr_client.fingerprint = self.current_fingerprint
result = await self.nostr_client.fetch_latest_snapshot()
if result:
manifest, chunks = result
@@ -4308,7 +4322,9 @@ class PasswordManager:
parent_seed=getattr(self, "parent_seed", None),
)
if getattr(self, "manifest_id", None):
if getattr(self, "manifest_id", None) and hasattr(
self.nostr_client, "_state_lock"
):
from nostr.backup_models import Manifest
with self.nostr_client._state_lock:

View File

@@ -0,0 +1,49 @@
from pathlib import Path
import main
from helpers import create_vault, dummy_nostr_client, TEST_SEED, TEST_PASSWORD
from seedpass.core.entry_management import EntryManager
from seedpass.core.backup import BackupManager
from seedpass.core.config_manager import ConfigManager
from seedpass.core.manager import PasswordManager, EncryptionMode
def _init_pm(dir_path: Path, client) -> PasswordManager:
vault, enc_mgr = create_vault(dir_path, TEST_SEED, TEST_PASSWORD)
cfg_mgr = ConfigManager(vault, dir_path)
backup_mgr = BackupManager(dir_path, cfg_mgr)
entry_mgr = EntryManager(vault, backup_mgr)
pm = PasswordManager.__new__(PasswordManager)
pm.encryption_mode = EncryptionMode.SEED_ONLY
pm.encryption_manager = enc_mgr
pm.vault = vault
pm.entry_manager = entry_mgr
pm.backup_manager = backup_mgr
pm.config_manager = cfg_mgr
pm.nostr_client = client
pm.fingerprint_dir = dir_path
pm.current_fingerprint = "fp"
pm.is_dirty = False
return pm
def test_restore_flow_from_snapshot(monkeypatch, tmp_path):
client, relay = dummy_nostr_client.__wrapped__(tmp_path / "srv", monkeypatch)
dir_a = tmp_path / "A"
dir_b = tmp_path / "B"
dir_a.mkdir()
dir_b.mkdir()
pm_a = _init_pm(dir_a, client)
pm_a.entry_manager.add_entry("site1", 12)
pm_a.sync_vault()
assert relay.manifests
pm_b = _init_pm(dir_b, client)
monkeypatch.setattr(main, "pause", lambda *a, **k: None)
main.handle_retrieve_from_nostr(pm_b)
labels = [e[1] for e in pm_b.entry_manager.list_entries()]
assert labels == ["site1"]