mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 15:58:48 +00:00
Add entry actions menu
This commit is contained in:
@@ -1542,6 +1542,93 @@ class PasswordManager:
|
|||||||
self.is_dirty = True
|
self.is_dirty = True
|
||||||
self.last_update = time.time()
|
self.last_update = time.time()
|
||||||
|
|
||||||
|
def _entry_actions_menu(self, index: int, entry: dict) -> None:
|
||||||
|
"""Provide actions for a retrieved entry."""
|
||||||
|
while True:
|
||||||
|
archived = entry.get("archived", entry.get("blacklisted", False))
|
||||||
|
print(colored("\n[+] Entry Actions:", "green"))
|
||||||
|
if archived:
|
||||||
|
print(colored("U. Unarchive", "cyan"))
|
||||||
|
else:
|
||||||
|
print(colored("A. Archive", "cyan"))
|
||||||
|
print(colored("N. Add Note", "cyan"))
|
||||||
|
print(colored("C. Add Custom Field", "cyan"))
|
||||||
|
print(colored("H. Add Hidden Field", "cyan"))
|
||||||
|
print(colored("E. Edit", "cyan"))
|
||||||
|
|
||||||
|
choice = (
|
||||||
|
input("Select an action or press Enter to return: ").strip().lower()
|
||||||
|
)
|
||||||
|
if not choice:
|
||||||
|
break
|
||||||
|
if choice == "a" and not archived:
|
||||||
|
self.entry_manager.archive_entry(index)
|
||||||
|
self.is_dirty = True
|
||||||
|
self.last_update = time.time()
|
||||||
|
elif choice == "u" and archived:
|
||||||
|
self.entry_manager.restore_entry(index)
|
||||||
|
self.is_dirty = True
|
||||||
|
self.last_update = time.time()
|
||||||
|
elif choice == "n":
|
||||||
|
note = input("Enter note: ").strip()
|
||||||
|
if note:
|
||||||
|
notes = entry.get("notes", "")
|
||||||
|
notes = f"{notes}\n{note}" if notes else note
|
||||||
|
self.entry_manager.modify_entry(index, notes=notes)
|
||||||
|
self.is_dirty = True
|
||||||
|
self.last_update = time.time()
|
||||||
|
elif choice in {"c", "h"}:
|
||||||
|
label = input(" Field label: ").strip()
|
||||||
|
if not label:
|
||||||
|
print(colored("Field label cannot be empty.", "red"))
|
||||||
|
else:
|
||||||
|
value = input(" Field value: ").strip()
|
||||||
|
hidden = choice == "h"
|
||||||
|
custom_fields = entry.get("custom_fields", [])
|
||||||
|
custom_fields.append(
|
||||||
|
{"label": label, "value": value, "is_hidden": hidden}
|
||||||
|
)
|
||||||
|
self.entry_manager.modify_entry(index, custom_fields=custom_fields)
|
||||||
|
self.is_dirty = True
|
||||||
|
self.last_update = time.time()
|
||||||
|
elif choice == "e":
|
||||||
|
self._entry_edit_menu(index, entry)
|
||||||
|
else:
|
||||||
|
print(colored("Invalid choice.", "red"))
|
||||||
|
entry = self.entry_manager.retrieve_entry(index) or entry
|
||||||
|
|
||||||
|
def _entry_edit_menu(self, index: int, entry: dict) -> None:
|
||||||
|
"""Sub-menu for editing common entry fields."""
|
||||||
|
entry_type = entry.get("type", EntryType.PASSWORD.value)
|
||||||
|
while True:
|
||||||
|
print(colored("\n[+] Edit Menu:", "green"))
|
||||||
|
print(colored("L. Edit Label", "cyan"))
|
||||||
|
if entry_type == EntryType.PASSWORD.value:
|
||||||
|
print(colored("U. Edit Username", "cyan"))
|
||||||
|
print(colored("R. Edit URL", "cyan"))
|
||||||
|
choice = input("Select option or press Enter to go back: ").strip().lower()
|
||||||
|
if not choice:
|
||||||
|
break
|
||||||
|
if choice == "l":
|
||||||
|
new_label = input("New label: ").strip()
|
||||||
|
if new_label:
|
||||||
|
self.entry_manager.modify_entry(index, label=new_label)
|
||||||
|
self.is_dirty = True
|
||||||
|
self.last_update = time.time()
|
||||||
|
elif entry_type == EntryType.PASSWORD.value and choice == "u":
|
||||||
|
new_username = input("New username: ").strip()
|
||||||
|
self.entry_manager.modify_entry(index, username=new_username)
|
||||||
|
self.is_dirty = True
|
||||||
|
self.last_update = time.time()
|
||||||
|
elif entry_type == EntryType.PASSWORD.value and choice == "r":
|
||||||
|
new_url = input("New URL: ").strip()
|
||||||
|
self.entry_manager.modify_entry(index, url=new_url)
|
||||||
|
self.is_dirty = True
|
||||||
|
self.last_update = time.time()
|
||||||
|
else:
|
||||||
|
print(colored("Invalid choice.", "red"))
|
||||||
|
entry = self.entry_manager.retrieve_entry(index) or entry
|
||||||
|
|
||||||
def handle_retrieve_entry(self) -> None:
|
def handle_retrieve_entry(self) -> None:
|
||||||
"""
|
"""
|
||||||
Handles retrieving a password from the index by prompting the user for the index number
|
Handles retrieving a password from the index by prompting the user for the index number
|
||||||
@@ -1625,7 +1712,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error generating TOTP code: {e}", exc_info=True)
|
logging.error(f"Error generating TOTP code: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to generate TOTP code: {e}", "red"))
|
print(colored(f"Error: Failed to generate TOTP code: {e}", "red"))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
if entry_type == EntryType.SSH.value:
|
if entry_type == EntryType.SSH.value:
|
||||||
@@ -1661,7 +1748,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error deriving SSH key pair: {e}", exc_info=True)
|
logging.error(f"Error deriving SSH key pair: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to derive SSH keys: {e}", "red"))
|
print(colored(f"Error: Failed to derive SSH keys: {e}", "red"))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
if entry_type == EntryType.SEED.value:
|
if entry_type == EntryType.SEED.value:
|
||||||
@@ -1712,7 +1799,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error deriving seed phrase: {e}", exc_info=True)
|
logging.error(f"Error deriving seed phrase: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to derive seed phrase: {e}", "red"))
|
print(colored(f"Error: Failed to derive seed phrase: {e}", "red"))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
if entry_type == EntryType.PGP.value:
|
if entry_type == EntryType.PGP.value:
|
||||||
@@ -1746,7 +1833,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error deriving PGP key: {e}", exc_info=True)
|
logging.error(f"Error deriving PGP key: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to derive PGP key: {e}", "red"))
|
print(colored(f"Error: Failed to derive PGP key: {e}", "red"))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
if entry_type == EntryType.NOSTR.value:
|
if entry_type == EntryType.NOSTR.value:
|
||||||
@@ -1780,7 +1867,7 @@ class PasswordManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error deriving Nostr keys: {e}", exc_info=True)
|
logging.error(f"Error deriving Nostr keys: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to derive Nostr keys: {e}", "red"))
|
print(colored(f"Error: Failed to derive Nostr keys: {e}", "red"))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1837,7 +1924,7 @@ class PasswordManager:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
print(colored(f" {f_label}: {f_value}", "cyan"))
|
print(colored(f" {f_label}: {f_value}", "cyan"))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
if entry_type == EntryType.MANAGED_ACCOUNT.value:
|
if entry_type == EntryType.MANAGED_ACCOUNT.value:
|
||||||
@@ -1881,13 +1968,13 @@ class PasswordManager:
|
|||||||
from password_manager.seedqr import encode_seedqr
|
from password_manager.seedqr import encode_seedqr
|
||||||
|
|
||||||
TotpManager.print_qr_code(encode_seedqr(seed))
|
TotpManager.print_qr_code(encode_seedqr(seed))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
if action == "l":
|
if action == "l":
|
||||||
self.load_managed_account(index)
|
self.load_managed_account(index)
|
||||||
return
|
return
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1975,7 +2062,7 @@ class PasswordManager:
|
|||||||
print(colored(f" {label}: {value}", "cyan"))
|
print(colored(f" {label}: {value}", "cyan"))
|
||||||
else:
|
else:
|
||||||
print(colored("Error: Failed to retrieve the password.", "red"))
|
print(colored("Error: Failed to retrieve the password.", "red"))
|
||||||
self._prompt_toggle_archive(entry, index)
|
self._entry_actions_menu(index, entry)
|
||||||
pause()
|
pause()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error during password retrieval: {e}", exc_info=True)
|
logging.error(f"Error during password retrieval: {e}", exc_info=True)
|
||||||
@@ -2625,7 +2712,7 @@ class PasswordManager:
|
|||||||
.lower()
|
.lower()
|
||||||
)
|
)
|
||||||
if action == "v":
|
if action == "v":
|
||||||
self.display_entry_details(entry_index)
|
self.show_entry_details_by_index(entry_index)
|
||||||
pause()
|
pause()
|
||||||
elif action == "r":
|
elif action == "r":
|
||||||
self.entry_manager.restore_entry(entry_index)
|
self.entry_manager.restore_entry(entry_index)
|
||||||
|
@@ -40,7 +40,7 @@ def test_archive_entry_from_retrieve(monkeypatch):
|
|||||||
|
|
||||||
index = entry_mgr.add_entry("example.com", 8)
|
index = entry_mgr.add_entry("example.com", 8)
|
||||||
|
|
||||||
inputs = iter([str(index), "y"])
|
inputs = iter([str(index), "a", ""])
|
||||||
monkeypatch.setattr("builtins.input", lambda *a, **k: next(inputs))
|
monkeypatch.setattr("builtins.input", lambda *a, **k: next(inputs))
|
||||||
|
|
||||||
pm.handle_retrieve_entry()
|
pm.handle_retrieve_entry()
|
||||||
@@ -72,7 +72,7 @@ def test_restore_entry_from_retrieve(monkeypatch):
|
|||||||
index = entry_mgr.add_entry("example.com", 8)
|
index = entry_mgr.add_entry("example.com", 8)
|
||||||
entry_mgr.archive_entry(index)
|
entry_mgr.archive_entry(index)
|
||||||
|
|
||||||
inputs = iter([str(index), "y"])
|
inputs = iter([str(index), "u", ""])
|
||||||
monkeypatch.setattr("builtins.input", lambda *a, **k: next(inputs))
|
monkeypatch.setattr("builtins.input", lambda *a, **k: next(inputs))
|
||||||
|
|
||||||
pm.handle_retrieve_entry()
|
pm.handle_retrieve_entry()
|
||||||
|
@@ -57,7 +57,7 @@ def test_manager_workflow(monkeypatch):
|
|||||||
"n", # add custom field
|
"n", # add custom field
|
||||||
"", # length (default)
|
"", # length (default)
|
||||||
"0", # retrieve index
|
"0", # retrieve index
|
||||||
"n", # archive entry prompt
|
"", # no action in entry menu
|
||||||
"0", # modify index
|
"0", # modify index
|
||||||
"", # new label
|
"", # new label
|
||||||
"user", # new username
|
"user", # new username
|
||||||
|
Reference in New Issue
Block a user