From eace2d95c5bd1562647f214820810e248232cfa6 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:11:13 -0400 Subject: [PATCH] Add archived flag to entries --- src/password_manager/entry_management.py | 50 +++++++++++++++----- src/password_manager/manager.py | 18 +++---- src/tests/test_entry_add.py | 2 +- src/tests/test_manager_add_totp.py | 1 + src/tests/test_manager_display_totp_codes.py | 4 +- src/tests/test_nostr_entry.py | 1 + src/tests/test_ssh_entry.py | 1 + src/tests/test_totp_entry.py | 2 + 8 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/password_manager/entry_management.py b/src/password_manager/entry_management.py index 80acb09..d24c29c 100644 --- a/src/password_manager/entry_management.py +++ b/src/password_manager/entry_management.py @@ -72,6 +72,9 @@ class EntryManager: and entry.get("type") == EntryType.PASSWORD.value ): entry.pop("website", None) + if "archived" not in entry and "blacklisted" in entry: + entry["archived"] = entry["blacklisted"] + entry.pop("blacklisted", None) logger.debug("Index loaded successfully.") return data except Exception as e: @@ -117,7 +120,7 @@ class EntryManager: length: int, username: Optional[str] = None, url: Optional[str] = None, - blacklisted: bool = False, + archived: bool = False, notes: str = "", custom_fields: List[Dict[str, Any]] | None = None, ) -> int: @@ -128,7 +131,7 @@ class EntryManager: :param length: The desired length of the password. :param username: (Optional) The username associated with the website. :param url: (Optional) The URL of the website. - :param blacklisted: (Optional) Whether the password is blacklisted. Defaults to False. + :param archived: (Optional) Whether the entry is archived. Defaults to False. :param notes: (Optional) Extra notes to attach to the entry. :return: The assigned index of the new entry. """ @@ -142,7 +145,7 @@ class EntryManager: "length": length, "username": username if username else "", "url": url if url else "", - "blacklisted": blacklisted, + "archived": archived, "type": EntryType.PASSWORD.value, "kind": EntryType.PASSWORD.value, "notes": notes, @@ -184,6 +187,7 @@ class EntryManager: label: str, parent_seed: str, *, + archived: bool = False, secret: str | None = None, index: int | None = None, period: int = 30, @@ -205,6 +209,7 @@ class EntryManager: "index": index, "period": period, "digits": digits, + "archived": archived, } else: entry = { @@ -214,6 +219,7 @@ class EntryManager: "secret": secret, "period": period, "digits": digits, + "archived": archived, } data["entries"][str(entry_id)] = entry @@ -234,6 +240,7 @@ class EntryManager: parent_seed: str, index: int | None = None, notes: str = "", + archived: bool = False, ) -> int: """Add a new SSH key pair entry. @@ -253,6 +260,7 @@ class EntryManager: "index": index, "label": label, "notes": notes, + "archived": archived, } self._save_index(data) self.update_checksum() @@ -281,6 +289,7 @@ class EntryManager: key_type: str = "ed25519", user_id: str = "", notes: str = "", + archived: bool = False, ) -> int: """Add a new PGP key entry.""" @@ -297,6 +306,7 @@ class EntryManager: "key_type": key_type, "user_id": user_id, "notes": notes, + "archived": archived, } self._save_index(data) self.update_checksum() @@ -329,6 +339,7 @@ class EntryManager: label: str, index: int | None = None, notes: str = "", + archived: bool = False, ) -> int: """Add a new Nostr key pair entry.""" @@ -343,6 +354,7 @@ class EntryManager: "index": index, "label": label, "notes": notes, + "archived": archived, } self._save_index(data) self.update_checksum() @@ -381,6 +393,7 @@ class EntryManager: index: int | None = None, words_num: int = 24, notes: str = "", + archived: bool = False, ) -> int: """Add a new derived seed phrase entry.""" @@ -396,6 +409,7 @@ class EntryManager: "label": label, "words": words_num, "notes": notes, + "archived": archived, } self._save_index(data) self.update_checksum() @@ -505,13 +519,14 @@ class EntryManager: index: int, username: Optional[str] = None, url: Optional[str] = None, - blacklisted: Optional[bool] = None, + archived: Optional[bool] = None, notes: Optional[str] = None, *, label: Optional[str] = None, period: Optional[int] = None, digits: Optional[int] = None, custom_fields: List[Dict[str, Any]] | None = None, + **legacy, ) -> None: """ Modifies an existing entry based on the provided index and new values. @@ -519,7 +534,7 @@ class EntryManager: :param index: The index number of the entry to modify. :param username: (Optional) The new username (password entries). :param url: (Optional) The new URL (password entries). - :param blacklisted: (Optional) The new blacklist status. + :param archived: (Optional) The new archived status. :param notes: (Optional) New notes to attach to the entry. :param label: (Optional) The new label for the entry. :param period: (Optional) The new TOTP period in seconds. @@ -564,10 +579,15 @@ class EntryManager: entry["url"] = url logger.debug(f"Updated URL to '{url}' for index {index}.") - if blacklisted is not None: - entry["blacklisted"] = blacklisted + if archived is None and "blacklisted" in legacy: + archived = legacy["blacklisted"] + + if archived is not None: + entry["archived"] = archived + if "blacklisted" in entry: + entry.pop("blacklisted", None) logger.debug( - f"Updated blacklist status to '{blacklisted}' for index {index}." + f"Updated archived status to '{archived}' for index {index}." ) if notes is not None: @@ -598,6 +618,14 @@ class EntryManager: colored(f"Error: Failed to modify entry at index {index}: {e}", "red") ) + def archive_entry(self, index: int) -> None: + """Mark the specified entry as archived.""" + self.modify_entry(index, archived=True) + + def restore_entry(self, index: int) -> None: + """Unarchive the specified entry.""" + self.modify_entry(index, archived=False) + def list_entries( self, sort_by: str = "index", filter_kind: str | None = None ) -> List[Tuple[int, str, Optional[str], Optional[str], bool]]: @@ -644,7 +672,7 @@ class EntryManager: label, entry.get("username", ""), entry.get("url", ""), - entry.get("blacklisted", False), + entry.get("archived", entry.get("blacklisted", False)), ) ) else: @@ -677,7 +705,7 @@ class EntryManager: print(colored(f" URL: {entry.get('url') or 'N/A'}", "cyan")) print( colored( - f" Blacklisted: {'Yes' if entry.get('blacklisted', False) else 'No'}", + f" Blacklisted: {'Yes' if entry.get('archived', entry.get('blacklisted', False)) else 'No'}", "cyan", ) ) @@ -739,7 +767,7 @@ class EntryManager: label, username, url, - entry.get("blacklisted", False), + entry.get("archived", entry.get("blacklisted", False)), ) ) else: diff --git a/src/password_manager/manager.py b/src/password_manager/manager.py index cb76a02..efbc439 100644 --- a/src/password_manager/manager.py +++ b/src/password_manager/manager.py @@ -939,7 +939,7 @@ class PasswordManager: length, username, url, - blacklisted=False, + archived=False, notes=notes, custom_fields=custom_fields, ) @@ -1545,7 +1545,7 @@ class PasswordManager: length = entry.get("length") username = entry.get("username") url = entry.get("url") - blacklisted = entry.get("blacklisted") + blacklisted = entry.get("archived", entry.get("blacklisted")) notes = entry.get("notes", "") print( @@ -1660,7 +1660,7 @@ class PasswordManager: label = entry.get("label", "") period = int(entry.get("period", 30)) digits = int(entry.get("digits", 6)) - blacklisted = entry.get("blacklisted", False) + blacklisted = entry.get("archived", entry.get("blacklisted", False)) notes = entry.get("notes", "") print( @@ -1751,7 +1751,7 @@ class PasswordManager: self.entry_manager.modify_entry( index, - blacklisted=new_blacklisted, + archived=new_blacklisted, notes=new_notes, label=new_label, period=new_period, @@ -1762,7 +1762,7 @@ class PasswordManager: website_name = entry.get("label", entry.get("website")) username = entry.get("username") url = entry.get("url") - blacklisted = entry.get("blacklisted") + blacklisted = entry.get("archived", entry.get("blacklisted")) notes = entry.get("notes", "") print( @@ -1847,8 +1847,8 @@ class PasswordManager: index, new_username, new_url, - new_blacklisted, - new_notes, + archived=new_blacklisted, + notes=new_notes, label=new_label, custom_fields=custom_fields, ) @@ -1992,7 +1992,7 @@ class PasswordManager: website = entry.get("label", entry.get("website", "")) username = entry.get("username", "") url = entry.get("url", "") - blacklisted = entry.get("blacklisted", False) + blacklisted = entry.get("archived", entry.get("blacklisted", False)) print(color_text(f" Label: {website}", "index")) print(color_text(f" Username: {username or 'N/A'}", "index")) print(color_text(f" URL: {url or 'N/A'}", "index")) @@ -2115,7 +2115,7 @@ class PasswordManager: totp_list: list[tuple[str, int, int, bool]] = [] for idx_str, entry in entries.items(): if entry.get("type") == EntryType.TOTP.value and not entry.get( - "blacklisted", False + "archived", entry.get("blacklisted", False) ): label = entry.get("label", "") period = int(entry.get("period", 30)) diff --git a/src/tests/test_entry_add.py b/src/tests/test_entry_add.py index f44f318..5388ff3 100644 --- a/src/tests/test_entry_add.py +++ b/src/tests/test_entry_add.py @@ -34,7 +34,7 @@ def test_add_and_retrieve_entry(): "length": 12, "username": "user", "url": "", - "blacklisted": False, + "archived": False, "type": "password", "kind": "password", "notes": "", diff --git a/src/tests/test_manager_add_totp.py b/src/tests/test_manager_add_totp.py index 4b159ad..8ad618b 100644 --- a/src/tests/test_manager_add_totp.py +++ b/src/tests/test_manager_add_totp.py @@ -63,5 +63,6 @@ def test_handle_add_totp(monkeypatch, capsys): "index": 0, "period": 30, "digits": 6, + "archived": False, } assert "ID 0" in out diff --git a/src/tests/test_manager_display_totp_codes.py b/src/tests/test_manager_display_totp_codes.py index b2773e0..649bcd2 100644 --- a/src/tests/test_manager_display_totp_codes.py +++ b/src/tests/test_manager_display_totp_codes.py @@ -61,7 +61,7 @@ def test_handle_display_totp_codes(monkeypatch, capsys): assert "123456" in out -def test_display_totp_codes_excludes_blacklisted(monkeypatch, capsys): +def test_display_totp_codes_excludes_archived(monkeypatch, capsys): with TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) @@ -83,7 +83,7 @@ def test_display_totp_codes_excludes_blacklisted(monkeypatch, capsys): entry_mgr.add_totp("Visible", TEST_SEED) entry_mgr.add_totp("Hidden", TEST_SEED) - entry_mgr.modify_entry(1, blacklisted=True) + entry_mgr.modify_entry(1, archived=True) monkeypatch.setattr(pm.entry_manager, "get_totp_code", lambda *a, **k: "123456") monkeypatch.setattr( diff --git a/src/tests/test_nostr_entry.py b/src/tests/test_nostr_entry.py index cfedc8f..569dfeb 100644 --- a/src/tests/test_nostr_entry.py +++ b/src/tests/test_nostr_entry.py @@ -30,6 +30,7 @@ def test_nostr_key_determinism(): "index": idx, "label": "main", "notes": "", + "archived": False, } npub1, nsec1 = entry_mgr.get_nostr_key_pair(idx, TEST_SEED) diff --git a/src/tests/test_ssh_entry.py b/src/tests/test_ssh_entry.py index 7a2f552..695034c 100644 --- a/src/tests/test_ssh_entry.py +++ b/src/tests/test_ssh_entry.py @@ -28,6 +28,7 @@ def test_add_and_retrieve_ssh_key_pair(): "index": index, "label": "ssh", "notes": "", + "archived": False, } priv1, pub1 = entry_mgr.get_ssh_key_pair(index, TEST_SEED) diff --git a/src/tests/test_totp_entry.py b/src/tests/test_totp_entry.py index 2b6b301..87f24da 100644 --- a/src/tests/test_totp_entry.py +++ b/src/tests/test_totp_entry.py @@ -35,6 +35,7 @@ def test_add_totp_and_get_code(): "index": 0, "period": 30, "digits": 6, + "archived": False, } code = entry_mgr.get_totp_code(0, TEST_SEED, timestamp=0) @@ -72,6 +73,7 @@ def test_add_totp_imported(tmp_path): "secret": secret, "period": 30, "digits": 6, + "archived": False, } code = em.get_totp_code(0, timestamp=0) assert code == pyotp.TOTP(secret).at(0)