diff --git a/scripts/generate_test_profile.py b/scripts/generate_test_profile.py index 3b92f8b..2837227 100644 --- a/scripts/generate_test_profile.py +++ b/scripts/generate_test_profile.py @@ -46,7 +46,9 @@ import gzip DEFAULT_PASSWORD = "testpassword" -def initialize_profile(profile_name: str) -> tuple[str, EntryManager, Path, str]: +def initialize_profile( + profile_name: str, +) -> tuple[str, EntryManager, Path, str, ConfigManager]: """Create or load a profile and return the seed phrase, manager, directory and fingerprint.""" initialize_app() seed_txt = APP_DIR / f"{profile_name}_seed.txt" @@ -98,7 +100,7 @@ def initialize_profile(profile_name: str) -> tuple[str, EntryManager, Path, str] cfg_mgr.set_password_hash(hashed) backup_mgr = BackupManager(profile_dir, cfg_mgr) entry_mgr = EntryManager(vault, backup_mgr) - return seed_phrase, entry_mgr, profile_dir, fingerprint + return seed_phrase, entry_mgr, profile_dir, fingerprint, cfg_mgr def random_secret(length: int = 16) -> str: @@ -159,7 +161,7 @@ def main() -> None: ) args = parser.parse_args() - seed, entry_mgr, dir_path, fingerprint = initialize_profile(args.profile) + seed, entry_mgr, dir_path, fingerprint, cfg_mgr = initialize_profile(args.profile) print(f"Using profile directory: {dir_path}") print(f"Parent seed: {seed}") if fingerprint: @@ -173,6 +175,7 @@ def main() -> None: entry_mgr.vault.encryption_manager, fingerprint or dir_path.name, parent_seed=seed, + config_manager=cfg_mgr, ) asyncio.run(client.publish_snapshot(encrypted)) print("[+] Data synchronized to Nostr.") diff --git a/src/nostr/client.py b/src/nostr/client.py index 915ac8f..b37d6de 100644 --- a/src/nostr/client.py +++ b/src/nostr/client.py @@ -4,7 +4,7 @@ import base64 import json import logging import time -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, TYPE_CHECKING import hashlib import asyncio import gzip @@ -30,6 +30,9 @@ from password_manager.encryption import EncryptionManager from constants import MAX_RETRIES, RETRY_DELAY from utils.file_lock import exclusive_lock +if TYPE_CHECKING: # pragma: no cover - imported for type hints + from password_manager.config_manager import ConfigManager + # Backwards compatibility for tests that patch these symbols KeyManager = SeedPassKeyManager ClientBuilder = Client @@ -92,10 +95,12 @@ class NostrClient: relays: Optional[List[str]] = None, parent_seed: Optional[str] = None, offline_mode: bool = False, + config_manager: Optional["ConfigManager"] = None, ) -> None: self.encryption_manager = encryption_manager self.fingerprint = fingerprint self.fingerprint_dir = self.encryption_manager.fingerprint_dir + self.config_manager = config_manager if parent_seed is None: parent_seed = self.encryption_manager.decrypt_parent_seed() @@ -278,13 +283,16 @@ class NostrClient: return None if retries is None or delay is None: - from password_manager.config_manager import ConfigManager - from password_manager.vault import Vault + if self.config_manager is None: + from password_manager.config_manager import ConfigManager + from password_manager.vault import Vault - cfg_mgr = ConfigManager( - Vault(self.encryption_manager, self.fingerprint_dir), - self.fingerprint_dir, - ) + cfg_mgr = ConfigManager( + Vault(self.encryption_manager, self.fingerprint_dir), + self.fingerprint_dir, + ) + else: + cfg_mgr = self.config_manager cfg = cfg_mgr.load_config(require_pin=False) retries = int(cfg.get("nostr_max_retries", MAX_RETRIES)) delay = float(cfg.get("nostr_retry_delay", RETRY_DELAY)) diff --git a/src/password_manager/manager.py b/src/password_manager/manager.py index 548bb8e..50c7e17 100644 --- a/src/password_manager/manager.py +++ b/src/password_manager/manager.py @@ -544,6 +544,7 @@ class PasswordManager: self.nostr_client = NostrClient( encryption_manager=self.encryption_manager, fingerprint=self.current_fingerprint, + config_manager=getattr(self, "config_manager", None), parent_seed=getattr(self, "parent_seed", None), ) logging.info( @@ -1020,6 +1021,7 @@ class PasswordManager: fingerprint=self.current_fingerprint, relays=relay_list, offline_mode=self.offline_mode, + config_manager=self.config_manager, parent_seed=getattr(self, "parent_seed", None), ) @@ -3718,6 +3720,7 @@ class PasswordManager: encryption_manager=self.encryption_manager, fingerprint=self.current_fingerprint, relays=relay_list, + config_manager=self.config_manager, parent_seed=getattr(self, "parent_seed", None), ) diff --git a/src/password_manager/portable_backup.py b/src/password_manager/portable_backup.py index 3e27671..8731818 100644 --- a/src/password_manager/portable_backup.py +++ b/src/password_manager/portable_backup.py @@ -90,7 +90,11 @@ def export_backup( enc_file.write_bytes(encrypted) os.chmod(enc_file, 0o600) try: - client = NostrClient(vault.encryption_manager, vault.fingerprint_dir.name) + client = NostrClient( + vault.encryption_manager, + vault.fingerprint_dir.name, + config_manager=backup_manager.config_manager, + ) asyncio.run(client.publish_snapshot(encrypted)) except Exception: logger.error("Failed to publish backup via Nostr", exc_info=True) diff --git a/src/tests/test_generate_test_profile.py b/src/tests/test_generate_test_profile.py index 6313968..8b6da8c 100644 --- a/src/tests/test_generate_test_profile.py +++ b/src/tests/test_generate_test_profile.py @@ -24,7 +24,8 @@ def test_initialize_profile_creates_directories(monkeypatch): assert spec.loader is not None spec.loader.exec_module(gtp) - seed, mgr, dir_path, fingerprint = gtp.initialize_profile("test") + seed, mgr, dir_path, fingerprint, cfg_mgr = gtp.initialize_profile("test") + assert cfg_mgr is not None assert constants.APP_DIR.exists() assert (constants.APP_DIR / "test_seed.txt").exists()