From 0258a79952ba1d5fd0b08fed4115cd9dcee31945 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:33:01 -0400 Subject: [PATCH] Add view option for archived entries --- README.md | 2 +- src/password_manager/manager.py | 47 ++++++++++++++++++++++--------- src/tests/test_archive_restore.py | 35 ++++++++++++++++++++++- 3 files changed, 68 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 583bc7a..0a00a71 100644 --- a/README.md +++ b/README.md @@ -257,7 +257,7 @@ When choosing **Add Entry**, you can now select **Password**, **2FA (TOTP)**, 2. SeedPass will show the current label, period, digit count, and archived status. 3. Enter new values or press **Enter** to keep the existing settings. 4. The updated entry is saved back to your encrypted vault. -5. Archived entries are hidden from lists but can be restored from the **List Archived** menu. +5. Archived entries are hidden from lists but can be viewed or restored from the **List Archived** menu. ### Using Secret Mode diff --git a/src/password_manager/manager.py b/src/password_manager/manager.py index b83b9c3..7ac1cd8 100644 --- a/src/password_manager/manager.py +++ b/src/password_manager/manager.py @@ -2157,7 +2157,7 @@ class PasswordManager: print(colored(f"Error: Failed to archive entry: {e}", "red")) def handle_view_archived_entries(self) -> None: - """Display archived entries and optionally restore one.""" + """Display archived entries and optionally view or restore them.""" try: archived = self.entry_manager.list_entries(include_archived=True) archived = [e for e in archived if e[4]] @@ -2171,26 +2171,45 @@ class PasswordManager: "Main Menu > Archived Entries", ) print(colored("\n[+] Archived Entries:\n", "green")) - for idx, label, username, url, _ in archived: + for idx, label, _username, _url, _ in archived: print(colored(f"{idx}. {label}", "cyan")) idx_input = input( - "Enter index to restore or press Enter to go back: " + "Enter index to manage or press Enter to go back: " ).strip() if not idx_input: break - if not idx_input.isdigit(): + if not idx_input.isdigit() or int(idx_input) not in [ + e[0] for e in archived + ]: print(colored("Invalid index.", "red")) continue - restore_index = int(idx_input) - self.entry_manager.restore_entry(restore_index) - self.is_dirty = True - self.last_update = time.time() - pause() - archived = [e for e in archived if e[0] != restore_index] - if not archived: - print(colored("All entries restored.", "green")) - pause() - break + entry_index = int(idx_input) + while True: + action = ( + input( + "Enter 'v' to view details, 'r' to restore, or press Enter to go back: " + ) + .strip() + .lower() + ) + if action == "v": + self.display_entry_details(entry_index) + pause() + elif action == "r": + self.entry_manager.restore_entry(entry_index) + self.is_dirty = True + self.last_update = time.time() + pause() + archived = [e for e in archived if e[0] != entry_index] + if not archived: + print(colored("All entries restored.", "green")) + pause() + return + break + elif not action: + break + else: + print(colored("Invalid choice.", "red")) except Exception as e: logging.error(f"Error viewing archived entries: {e}", exc_info=True) print(colored(f"Error: Failed to view archived entries: {e}", "red")) diff --git a/src/tests/test_archive_restore.py b/src/tests/test_archive_restore.py index fe963db..d791077 100644 --- a/src/tests/test_archive_restore.py +++ b/src/tests/test_archive_restore.py @@ -74,7 +74,40 @@ def test_view_archived_entries_cli(monkeypatch): pm.handle_archive_entry() assert entry_mgr.retrieve_entry(idx)["archived"] is True - inputs = iter([str(idx), ""]) + inputs = iter([str(idx), "r", "", ""]) monkeypatch.setattr("builtins.input", lambda *_: next(inputs)) pm.handle_view_archived_entries() assert entry_mgr.retrieve_entry(idx)["archived"] is False + + +def test_view_archived_entries_view_only(monkeypatch, capsys): + with TemporaryDirectory() as tmpdir: + tmp_path = Path(tmpdir) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) + cfg_mgr = ConfigManager(vault, tmp_path) + backup_mgr = BackupManager(tmp_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.parent_seed = TEST_SEED + pm.nostr_client = SimpleNamespace() + pm.fingerprint_dir = tmp_path + pm.is_dirty = False + + idx = entry_mgr.add_entry("example.com", 8) + + monkeypatch.setattr("builtins.input", lambda *_: str(idx)) + pm.handle_archive_entry() + assert entry_mgr.retrieve_entry(idx)["archived"] is True + + inputs = iter([str(idx), "v", "", "", ""]) + monkeypatch.setattr("builtins.input", lambda *_: next(inputs)) + pm.handle_view_archived_entries() + assert entry_mgr.retrieve_entry(idx)["archived"] is True + out = capsys.readouterr().out + assert "example.com" in out