mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-07 14:58:56 +00:00
Merge pull request #611 from PR0M3TH3AN/codex/add-non-interactive-password-handling
Enable non-interactive vault unlock
This commit is contained in:
@@ -117,7 +117,9 @@ class PasswordManager:
|
||||
verification, ensuring the integrity and confidentiality of the stored password database.
|
||||
"""
|
||||
|
||||
def __init__(self, fingerprint: Optional[str] = None) -> None:
|
||||
def __init__(
|
||||
self, fingerprint: Optional[str] = None, *, password: Optional[str] = None
|
||||
) -> None:
|
||||
"""Initialize the PasswordManager.
|
||||
|
||||
Parameters
|
||||
@@ -161,7 +163,7 @@ class PasswordManager:
|
||||
|
||||
if fingerprint:
|
||||
# Load the specified profile without prompting
|
||||
self.select_fingerprint(fingerprint)
|
||||
self.select_fingerprint(fingerprint, password=password)
|
||||
else:
|
||||
# Ensure a parent seed is set up before accessing the fingerprint directory
|
||||
self.setup_parent_seed()
|
||||
@@ -187,6 +189,11 @@ class PasswordManager:
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_password_prompt() -> str:
|
||||
"""Return the standard prompt for requesting a master password."""
|
||||
return "Enter your master password: "
|
||||
|
||||
@property
|
||||
def parent_seed(self) -> Optional[str]:
|
||||
"""Return the decrypted parent seed if set."""
|
||||
@@ -269,12 +276,15 @@ class PasswordManager:
|
||||
self.config_manager = None
|
||||
self.locked = True
|
||||
|
||||
def unlock_vault(self) -> None:
|
||||
"""Prompt for password and reinitialize managers."""
|
||||
def unlock_vault(self, password: Optional[str] = None) -> None:
|
||||
"""Unlock the vault using ``password`` without prompting if provided."""
|
||||
start = time.perf_counter()
|
||||
if not self.fingerprint_dir:
|
||||
raise ValueError("Fingerprint directory not set")
|
||||
self.setup_encryption_manager(self.fingerprint_dir)
|
||||
if password is None:
|
||||
self.setup_encryption_manager(self.fingerprint_dir)
|
||||
else:
|
||||
self.setup_encryption_manager(self.fingerprint_dir, password)
|
||||
self.initialize_bip85()
|
||||
self.initialize_managers()
|
||||
self.locked = False
|
||||
@@ -394,7 +404,9 @@ class PasswordManager:
|
||||
print(colored(f"Error: Failed to add new seed profile: {e}", "red"))
|
||||
sys.exit(1)
|
||||
|
||||
def select_fingerprint(self, fingerprint: str) -> None:
|
||||
def select_fingerprint(
|
||||
self, fingerprint: str, *, password: Optional[str] = None
|
||||
) -> None:
|
||||
if self.fingerprint_manager.select_fingerprint(fingerprint):
|
||||
self.current_fingerprint = fingerprint # Add this line
|
||||
self.fingerprint_dir = (
|
||||
@@ -409,7 +421,7 @@ class PasswordManager:
|
||||
)
|
||||
sys.exit(1)
|
||||
# Setup the encryption manager and load parent seed
|
||||
self.setup_encryption_manager(self.fingerprint_dir)
|
||||
self.setup_encryption_manager(self.fingerprint_dir, password)
|
||||
# Initialize BIP85 and other managers
|
||||
self.initialize_bip85()
|
||||
self.initialize_managers()
|
||||
|
84
src/tests/test_noninteractive_init_unlock.py
Normal file
84
src/tests/test_noninteractive_init_unlock.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import importlib
|
||||
import bcrypt
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
import constants
|
||||
import password_manager.manager as manager_module
|
||||
from utils.fingerprint_manager import FingerprintManager
|
||||
from password_manager.config_manager import ConfigManager
|
||||
from tests.helpers import TEST_SEED, TEST_PASSWORD, create_vault
|
||||
|
||||
|
||||
def test_init_with_password(monkeypatch):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
tmp = Path(tmpdir)
|
||||
monkeypatch.setattr(Path, "home", lambda: tmp)
|
||||
importlib.reload(constants)
|
||||
importlib.reload(manager_module)
|
||||
|
||||
fm = FingerprintManager(constants.APP_DIR)
|
||||
fp = fm.add_fingerprint(TEST_SEED)
|
||||
dir_path = constants.APP_DIR / fp
|
||||
vault, _enc = create_vault(dir_path, TEST_SEED, TEST_PASSWORD)
|
||||
cfg = ConfigManager(vault, dir_path)
|
||||
cfg.set_password_hash(
|
||||
bcrypt.hashpw(TEST_PASSWORD.encode(), bcrypt.gensalt()).decode()
|
||||
)
|
||||
cfg.set_kdf_iterations(100_000)
|
||||
|
||||
called = {}
|
||||
|
||||
def fake_setup(self, path, pw=None, **_):
|
||||
called["password"] = pw
|
||||
return True
|
||||
|
||||
monkeypatch.setattr(
|
||||
manager_module.PasswordManager, "initialize_bip85", lambda self: None
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
manager_module.PasswordManager, "initialize_managers", lambda self: None
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
manager_module.PasswordManager, "setup_encryption_manager", fake_setup
|
||||
)
|
||||
|
||||
pm = manager_module.PasswordManager(fingerprint=fp, password=TEST_PASSWORD)
|
||||
assert called["password"] == TEST_PASSWORD
|
||||
|
||||
|
||||
def test_unlock_with_password(monkeypatch):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
tmp = Path(tmpdir)
|
||||
monkeypatch.setattr(Path, "home", lambda: tmp)
|
||||
importlib.reload(constants)
|
||||
importlib.reload(manager_module)
|
||||
|
||||
fm = FingerprintManager(constants.APP_DIR)
|
||||
fp = fm.add_fingerprint(TEST_SEED)
|
||||
dir_path = constants.APP_DIR / fp
|
||||
vault, _enc = create_vault(dir_path, TEST_SEED, TEST_PASSWORD)
|
||||
cfg = ConfigManager(vault, dir_path)
|
||||
cfg.set_password_hash(
|
||||
bcrypt.hashpw(TEST_PASSWORD.encode(), bcrypt.gensalt()).decode()
|
||||
)
|
||||
|
||||
pm = manager_module.PasswordManager.__new__(manager_module.PasswordManager)
|
||||
pm.fingerprint_dir = dir_path
|
||||
pm.config_manager = cfg
|
||||
pm.locked = True
|
||||
called = {}
|
||||
|
||||
def fake_setup(path, pw=None):
|
||||
called["password"] = pw
|
||||
|
||||
monkeypatch.setattr(
|
||||
manager_module.PasswordManager, "initialize_bip85", lambda self: None
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
manager_module.PasswordManager, "initialize_managers", lambda self: None
|
||||
)
|
||||
pm.setup_encryption_manager = fake_setup
|
||||
|
||||
pm.unlock_vault(TEST_PASSWORD)
|
||||
assert called["password"] == TEST_PASSWORD
|
Reference in New Issue
Block a user