Merge pull request #347 from PR0M3TH3AN/codex/add-archived-flag-to-entry-management

Add archived flag to password entries
This commit is contained in:
thePR0M3TH3AN
2025-07-07 10:18:16 -04:00
committed by GitHub
8 changed files with 56 additions and 23 deletions

View File

@@ -72,6 +72,9 @@ class EntryManager:
and entry.get("type") == EntryType.PASSWORD.value and entry.get("type") == EntryType.PASSWORD.value
): ):
entry.pop("website", None) 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.") logger.debug("Index loaded successfully.")
return data return data
except Exception as e: except Exception as e:
@@ -117,7 +120,7 @@ class EntryManager:
length: int, length: int,
username: Optional[str] = None, username: Optional[str] = None,
url: Optional[str] = None, url: Optional[str] = None,
blacklisted: bool = False, archived: bool = False,
notes: str = "", notes: str = "",
custom_fields: List[Dict[str, Any]] | None = None, custom_fields: List[Dict[str, Any]] | None = None,
) -> int: ) -> int:
@@ -128,7 +131,7 @@ class EntryManager:
:param length: The desired length of the password. :param length: The desired length of the password.
:param username: (Optional) The username associated with the website. :param username: (Optional) The username associated with the website.
:param url: (Optional) The URL of 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. :param notes: (Optional) Extra notes to attach to the entry.
:return: The assigned index of the new entry. :return: The assigned index of the new entry.
""" """
@@ -142,7 +145,7 @@ class EntryManager:
"length": length, "length": length,
"username": username if username else "", "username": username if username else "",
"url": url if url else "", "url": url if url else "",
"blacklisted": blacklisted, "archived": archived,
"type": EntryType.PASSWORD.value, "type": EntryType.PASSWORD.value,
"kind": EntryType.PASSWORD.value, "kind": EntryType.PASSWORD.value,
"notes": notes, "notes": notes,
@@ -184,6 +187,7 @@ class EntryManager:
label: str, label: str,
parent_seed: str, parent_seed: str,
*, *,
archived: bool = False,
secret: str | None = None, secret: str | None = None,
index: int | None = None, index: int | None = None,
period: int = 30, period: int = 30,
@@ -205,6 +209,7 @@ class EntryManager:
"index": index, "index": index,
"period": period, "period": period,
"digits": digits, "digits": digits,
"archived": archived,
} }
else: else:
entry = { entry = {
@@ -214,6 +219,7 @@ class EntryManager:
"secret": secret, "secret": secret,
"period": period, "period": period,
"digits": digits, "digits": digits,
"archived": archived,
} }
data["entries"][str(entry_id)] = entry data["entries"][str(entry_id)] = entry
@@ -234,6 +240,7 @@ class EntryManager:
parent_seed: str, parent_seed: str,
index: int | None = None, index: int | None = None,
notes: str = "", notes: str = "",
archived: bool = False,
) -> int: ) -> int:
"""Add a new SSH key pair entry. """Add a new SSH key pair entry.
@@ -253,6 +260,7 @@ class EntryManager:
"index": index, "index": index,
"label": label, "label": label,
"notes": notes, "notes": notes,
"archived": archived,
} }
self._save_index(data) self._save_index(data)
self.update_checksum() self.update_checksum()
@@ -281,6 +289,7 @@ class EntryManager:
key_type: str = "ed25519", key_type: str = "ed25519",
user_id: str = "", user_id: str = "",
notes: str = "", notes: str = "",
archived: bool = False,
) -> int: ) -> int:
"""Add a new PGP key entry.""" """Add a new PGP key entry."""
@@ -297,6 +306,7 @@ class EntryManager:
"key_type": key_type, "key_type": key_type,
"user_id": user_id, "user_id": user_id,
"notes": notes, "notes": notes,
"archived": archived,
} }
self._save_index(data) self._save_index(data)
self.update_checksum() self.update_checksum()
@@ -329,6 +339,7 @@ class EntryManager:
label: str, label: str,
index: int | None = None, index: int | None = None,
notes: str = "", notes: str = "",
archived: bool = False,
) -> int: ) -> int:
"""Add a new Nostr key pair entry.""" """Add a new Nostr key pair entry."""
@@ -343,6 +354,7 @@ class EntryManager:
"index": index, "index": index,
"label": label, "label": label,
"notes": notes, "notes": notes,
"archived": archived,
} }
self._save_index(data) self._save_index(data)
self.update_checksum() self.update_checksum()
@@ -381,6 +393,7 @@ class EntryManager:
index: int | None = None, index: int | None = None,
words_num: int = 24, words_num: int = 24,
notes: str = "", notes: str = "",
archived: bool = False,
) -> int: ) -> int:
"""Add a new derived seed phrase entry.""" """Add a new derived seed phrase entry."""
@@ -396,6 +409,7 @@ class EntryManager:
"label": label, "label": label,
"words": words_num, "words": words_num,
"notes": notes, "notes": notes,
"archived": archived,
} }
self._save_index(data) self._save_index(data)
self.update_checksum() self.update_checksum()
@@ -505,13 +519,14 @@ class EntryManager:
index: int, index: int,
username: Optional[str] = None, username: Optional[str] = None,
url: Optional[str] = None, url: Optional[str] = None,
blacklisted: Optional[bool] = None, archived: Optional[bool] = None,
notes: Optional[str] = None, notes: Optional[str] = None,
*, *,
label: Optional[str] = None, label: Optional[str] = None,
period: Optional[int] = None, period: Optional[int] = None,
digits: Optional[int] = None, digits: Optional[int] = None,
custom_fields: List[Dict[str, Any]] | None = None, custom_fields: List[Dict[str, Any]] | None = None,
**legacy,
) -> None: ) -> None:
""" """
Modifies an existing entry based on the provided index and new values. 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 index: The index number of the entry to modify.
:param username: (Optional) The new username (password entries). :param username: (Optional) The new username (password entries).
:param url: (Optional) The new URL (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 notes: (Optional) New notes to attach to the entry.
:param label: (Optional) The new label for the entry. :param label: (Optional) The new label for the entry.
:param period: (Optional) The new TOTP period in seconds. :param period: (Optional) The new TOTP period in seconds.
@@ -564,10 +579,15 @@ class EntryManager:
entry["url"] = url entry["url"] = url
logger.debug(f"Updated URL to '{url}' for index {index}.") logger.debug(f"Updated URL to '{url}' for index {index}.")
if blacklisted is not None: if archived is None and "blacklisted" in legacy:
entry["blacklisted"] = blacklisted archived = legacy["blacklisted"]
if archived is not None:
entry["archived"] = archived
if "blacklisted" in entry:
entry.pop("blacklisted", None)
logger.debug( 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: if notes is not None:
@@ -598,6 +618,14 @@ class EntryManager:
colored(f"Error: Failed to modify entry at index {index}: {e}", "red") 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( def list_entries(
self, sort_by: str = "index", filter_kind: str | None = None self, sort_by: str = "index", filter_kind: str | None = None
) -> List[Tuple[int, str, Optional[str], Optional[str], bool]]: ) -> List[Tuple[int, str, Optional[str], Optional[str], bool]]:
@@ -644,7 +672,7 @@ class EntryManager:
label, label,
entry.get("username", ""), entry.get("username", ""),
entry.get("url", ""), entry.get("url", ""),
entry.get("blacklisted", False), entry.get("archived", entry.get("blacklisted", False)),
) )
) )
else: else:
@@ -677,7 +705,7 @@ class EntryManager:
print(colored(f" URL: {entry.get('url') or 'N/A'}", "cyan")) print(colored(f" URL: {entry.get('url') or 'N/A'}", "cyan"))
print( print(
colored( 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", "cyan",
) )
) )
@@ -739,7 +767,7 @@ class EntryManager:
label, label,
username, username,
url, url,
entry.get("blacklisted", False), entry.get("archived", entry.get("blacklisted", False)),
) )
) )
else: else:

View File

@@ -939,7 +939,7 @@ class PasswordManager:
length, length,
username, username,
url, url,
blacklisted=False, archived=False,
notes=notes, notes=notes,
custom_fields=custom_fields, custom_fields=custom_fields,
) )
@@ -1545,7 +1545,7 @@ class PasswordManager:
length = entry.get("length") length = entry.get("length")
username = entry.get("username") username = entry.get("username")
url = entry.get("url") url = entry.get("url")
blacklisted = entry.get("blacklisted") blacklisted = entry.get("archived", entry.get("blacklisted"))
notes = entry.get("notes", "") notes = entry.get("notes", "")
print( print(
@@ -1660,7 +1660,7 @@ class PasswordManager:
label = entry.get("label", "") label = entry.get("label", "")
period = int(entry.get("period", 30)) period = int(entry.get("period", 30))
digits = int(entry.get("digits", 6)) digits = int(entry.get("digits", 6))
blacklisted = entry.get("blacklisted", False) blacklisted = entry.get("archived", entry.get("blacklisted", False))
notes = entry.get("notes", "") notes = entry.get("notes", "")
print( print(
@@ -1751,7 +1751,7 @@ class PasswordManager:
self.entry_manager.modify_entry( self.entry_manager.modify_entry(
index, index,
blacklisted=new_blacklisted, archived=new_blacklisted,
notes=new_notes, notes=new_notes,
label=new_label, label=new_label,
period=new_period, period=new_period,
@@ -1762,7 +1762,7 @@ class PasswordManager:
website_name = entry.get("label", entry.get("website")) website_name = entry.get("label", entry.get("website"))
username = entry.get("username") username = entry.get("username")
url = entry.get("url") url = entry.get("url")
blacklisted = entry.get("blacklisted") blacklisted = entry.get("archived", entry.get("blacklisted"))
notes = entry.get("notes", "") notes = entry.get("notes", "")
print( print(
@@ -1847,8 +1847,8 @@ class PasswordManager:
index, index,
new_username, new_username,
new_url, new_url,
new_blacklisted, archived=new_blacklisted,
new_notes, notes=new_notes,
label=new_label, label=new_label,
custom_fields=custom_fields, custom_fields=custom_fields,
) )
@@ -1992,7 +1992,7 @@ class PasswordManager:
website = entry.get("label", entry.get("website", "")) website = entry.get("label", entry.get("website", ""))
username = entry.get("username", "") username = entry.get("username", "")
url = entry.get("url", "") 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" Label: {website}", "index"))
print(color_text(f" Username: {username or 'N/A'}", "index")) print(color_text(f" Username: {username or 'N/A'}", "index"))
print(color_text(f" URL: {url 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]] = [] totp_list: list[tuple[str, int, int, bool]] = []
for idx_str, entry in entries.items(): for idx_str, entry in entries.items():
if entry.get("type") == EntryType.TOTP.value and not entry.get( if entry.get("type") == EntryType.TOTP.value and not entry.get(
"blacklisted", False "archived", entry.get("blacklisted", False)
): ):
label = entry.get("label", "") label = entry.get("label", "")
period = int(entry.get("period", 30)) period = int(entry.get("period", 30))

View File

@@ -34,7 +34,7 @@ def test_add_and_retrieve_entry():
"length": 12, "length": 12,
"username": "user", "username": "user",
"url": "", "url": "",
"blacklisted": False, "archived": False,
"type": "password", "type": "password",
"kind": "password", "kind": "password",
"notes": "", "notes": "",

View File

@@ -63,5 +63,6 @@ def test_handle_add_totp(monkeypatch, capsys):
"index": 0, "index": 0,
"period": 30, "period": 30,
"digits": 6, "digits": 6,
"archived": False,
} }
assert "ID 0" in out assert "ID 0" in out

View File

@@ -61,7 +61,7 @@ def test_handle_display_totp_codes(monkeypatch, capsys):
assert "123456" in out 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: with TemporaryDirectory() as tmpdir:
tmp_path = Path(tmpdir) tmp_path = Path(tmpdir)
vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) 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("Visible", TEST_SEED)
entry_mgr.add_totp("Hidden", 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(pm.entry_manager, "get_totp_code", lambda *a, **k: "123456")
monkeypatch.setattr( monkeypatch.setattr(

View File

@@ -30,6 +30,7 @@ def test_nostr_key_determinism():
"index": idx, "index": idx,
"label": "main", "label": "main",
"notes": "", "notes": "",
"archived": False,
} }
npub1, nsec1 = entry_mgr.get_nostr_key_pair(idx, TEST_SEED) npub1, nsec1 = entry_mgr.get_nostr_key_pair(idx, TEST_SEED)

View File

@@ -28,6 +28,7 @@ def test_add_and_retrieve_ssh_key_pair():
"index": index, "index": index,
"label": "ssh", "label": "ssh",
"notes": "", "notes": "",
"archived": False,
} }
priv1, pub1 = entry_mgr.get_ssh_key_pair(index, TEST_SEED) priv1, pub1 = entry_mgr.get_ssh_key_pair(index, TEST_SEED)

View File

@@ -35,6 +35,7 @@ def test_add_totp_and_get_code():
"index": 0, "index": 0,
"period": 30, "period": 30,
"digits": 6, "digits": 6,
"archived": False,
} }
code = entry_mgr.get_totp_code(0, TEST_SEED, timestamp=0) code = entry_mgr.get_totp_code(0, TEST_SEED, timestamp=0)
@@ -72,6 +73,7 @@ def test_add_totp_imported(tmp_path):
"secret": secret, "secret": secret,
"period": 30, "period": 30,
"digits": 6, "digits": 6,
"archived": False,
} }
code = em.get_totp_code(0, timestamp=0) code = em.get_totp_code(0, timestamp=0)
assert code == pyotp.TOTP(secret).at(0) assert code == pyotp.TOTP(secret).at(0)