mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-10 08:19:23 +00:00
Add list entries feature
This commit is contained in:
22
src/main.py
22
src/main.py
@@ -701,10 +701,11 @@ def display_menu(
|
|||||||
1. Add Entry
|
1. Add Entry
|
||||||
2. Retrieve Entry
|
2. Retrieve Entry
|
||||||
3. Search Entries
|
3. Search Entries
|
||||||
4. Modify an Existing Entry
|
4. List Entries
|
||||||
5. 2FA Codes
|
5. Modify an Existing Entry
|
||||||
6. Settings
|
6. 2FA Codes
|
||||||
7. Exit
|
7. Settings
|
||||||
|
8. Exit
|
||||||
"""
|
"""
|
||||||
display_fn = getattr(password_manager, "display_stats", None)
|
display_fn = getattr(password_manager, "display_stats", None)
|
||||||
if callable(display_fn):
|
if callable(display_fn):
|
||||||
@@ -729,7 +730,7 @@ def display_menu(
|
|||||||
print(colored(menu, "cyan"))
|
print(colored(menu, "cyan"))
|
||||||
try:
|
try:
|
||||||
choice = timed_input(
|
choice = timed_input(
|
||||||
"Enter your choice (1-7): ", inactivity_timeout
|
"Enter your choice (1-8): ", inactivity_timeout
|
||||||
).strip()
|
).strip()
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
print(colored("Session timed out. Vault locked.", "yellow"))
|
print(colored("Session timed out. Vault locked.", "yellow"))
|
||||||
@@ -740,7 +741,7 @@ def display_menu(
|
|||||||
if not choice:
|
if not choice:
|
||||||
print(
|
print(
|
||||||
colored(
|
colored(
|
||||||
"No input detected. Please enter a number between 1 and 7.",
|
"No input detected. Please enter a number between 1 and 8.",
|
||||||
"yellow",
|
"yellow",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -787,14 +788,17 @@ def display_menu(
|
|||||||
password_manager.handle_search_entries()
|
password_manager.handle_search_entries()
|
||||||
elif choice == "4":
|
elif choice == "4":
|
||||||
password_manager.update_activity()
|
password_manager.update_activity()
|
||||||
password_manager.handle_modify_entry()
|
password_manager.handle_list_entries()
|
||||||
elif choice == "5":
|
elif choice == "5":
|
||||||
password_manager.update_activity()
|
password_manager.update_activity()
|
||||||
password_manager.handle_display_totp_codes()
|
password_manager.handle_modify_entry()
|
||||||
elif choice == "6":
|
elif choice == "6":
|
||||||
password_manager.update_activity()
|
password_manager.update_activity()
|
||||||
handle_settings(password_manager)
|
password_manager.handle_display_totp_codes()
|
||||||
elif choice == "7":
|
elif choice == "7":
|
||||||
|
password_manager.update_activity()
|
||||||
|
handle_settings(password_manager)
|
||||||
|
elif choice == "8":
|
||||||
logging.info("Exiting the program.")
|
logging.info("Exiting the program.")
|
||||||
print(colored("Exiting the program.", "green"))
|
print(colored("Exiting the program.", "green"))
|
||||||
password_manager.nostr_client.close_client_pool()
|
password_manager.nostr_client.close_client_pool()
|
||||||
|
@@ -841,3 +841,39 @@ class EntryManager:
|
|||||||
logger.error(f"Failed to list all entries: {e}", exc_info=True)
|
logger.error(f"Failed to list all entries: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to list all entries: {e}", "red"))
|
print(colored(f"Error: Failed to list all entries: {e}", "red"))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def get_entry_summaries(
|
||||||
|
self, filter_kind: str | None = None
|
||||||
|
) -> list[tuple[int, str]]:
|
||||||
|
"""Return a list of entry index and display labels."""
|
||||||
|
try:
|
||||||
|
data = self.vault.load_index()
|
||||||
|
entries_data = data.get("entries", {})
|
||||||
|
|
||||||
|
summaries: list[tuple[int, str]] = []
|
||||||
|
for idx_str, entry in entries_data.items():
|
||||||
|
etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
|
||||||
|
if filter_kind and etype != filter_kind:
|
||||||
|
continue
|
||||||
|
if etype == EntryType.PASSWORD.value:
|
||||||
|
label = entry.get("website", "")
|
||||||
|
elif etype == EntryType.TOTP.value:
|
||||||
|
label = entry.get("label", "")
|
||||||
|
elif etype == EntryType.SSH.value:
|
||||||
|
label = "SSH Key"
|
||||||
|
elif etype == EntryType.SEED.value:
|
||||||
|
label = "Seed Phrase"
|
||||||
|
elif etype == EntryType.NOSTR.value:
|
||||||
|
label = entry.get("label", "Nostr Key")
|
||||||
|
elif etype == EntryType.PGP.value:
|
||||||
|
label = "PGP Key"
|
||||||
|
else:
|
||||||
|
label = etype
|
||||||
|
summaries.append((int(idx_str), label))
|
||||||
|
|
||||||
|
summaries.sort(key=lambda x: x[0])
|
||||||
|
return summaries
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get entry summaries: {e}", exc_info=True)
|
||||||
|
print(colored(f"Error: Failed to get entry summaries: {e}", "red"))
|
||||||
|
return []
|
||||||
|
@@ -1752,93 +1752,126 @@ class PasswordManager:
|
|||||||
|
|
||||||
print(colored("\n[+] Search Results:\n", "green"))
|
print(colored("\n[+] Search Results:\n", "green"))
|
||||||
for match in results:
|
for match in results:
|
||||||
index, website, username, url, blacklisted = match
|
self.display_entry_details(match[0])
|
||||||
entry = self.entry_manager.retrieve_entry(index)
|
|
||||||
if not entry:
|
|
||||||
continue
|
|
||||||
etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
|
|
||||||
print(colored(f"Index: {index}", "cyan"))
|
|
||||||
if etype == EntryType.TOTP.value:
|
|
||||||
print(colored(f" Label: {entry.get('label', website)}", "cyan"))
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Derivation Index: {entry.get('index', index)}", "cyan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Period: {entry.get('period', 30)}s Digits: {entry.get('digits', 6)}",
|
|
||||||
"cyan",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
notes = entry.get("notes", "")
|
|
||||||
if notes:
|
|
||||||
print(colored(f" Notes: {notes}", "cyan"))
|
|
||||||
elif etype == EntryType.SEED.value:
|
|
||||||
print(colored(" Type: Seed Phrase", "cyan"))
|
|
||||||
print(colored(f" Words: {entry.get('words', 24)}", "cyan"))
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Derivation Index: {entry.get('index', index)}", "cyan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
notes = entry.get("notes", "")
|
|
||||||
if notes:
|
|
||||||
print(colored(f" Notes: {notes}", "cyan"))
|
|
||||||
elif etype == EntryType.SSH.value:
|
|
||||||
print(colored(" Type: SSH Key", "cyan"))
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Derivation Index: {entry.get('index', index)}", "cyan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
notes = entry.get("notes", "")
|
|
||||||
if notes:
|
|
||||||
print(colored(f" Notes: {notes}", "cyan"))
|
|
||||||
elif etype == EntryType.PGP.value:
|
|
||||||
print(colored(" Type: PGP Key", "cyan"))
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Key Type: {entry.get('key_type', 'ed25519')}", "cyan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
uid = entry.get("user_id", "")
|
|
||||||
if uid:
|
|
||||||
print(colored(f" User ID: {uid}", "cyan"))
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Derivation Index: {entry.get('index', index)}", "cyan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
notes = entry.get("notes", "")
|
|
||||||
if notes:
|
|
||||||
print(colored(f" Notes: {notes}", "cyan"))
|
|
||||||
elif etype == EntryType.NOSTR.value:
|
|
||||||
print(colored(" Type: Nostr Key", "cyan"))
|
|
||||||
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Derivation Index: {entry.get('index', index)}", "cyan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
notes = entry.get("notes", "")
|
|
||||||
if notes:
|
|
||||||
print(colored(f" Notes: {notes}", "cyan"))
|
|
||||||
else:
|
|
||||||
print(colored(f" Website: {website}", "cyan"))
|
|
||||||
print(colored(f" Username: {username or 'N/A'}", "cyan"))
|
|
||||||
print(colored(f" URL: {url or 'N/A'}", "cyan"))
|
|
||||||
print(
|
|
||||||
colored(
|
|
||||||
f" Blacklisted: {'Yes' if blacklisted else 'No'}", "cyan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
print("-" * 40)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Failed to search entries: {e}", exc_info=True)
|
logging.error(f"Failed to search entries: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to search entries: {e}", "red"))
|
print(colored(f"Error: Failed to search entries: {e}", "red"))
|
||||||
|
|
||||||
|
def display_entry_details(self, index: int) -> None:
|
||||||
|
"""Print detailed information for a single entry."""
|
||||||
|
entry = self.entry_manager.retrieve_entry(index)
|
||||||
|
if not entry:
|
||||||
|
return
|
||||||
|
|
||||||
|
etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
|
||||||
|
print(colored(f"Index: {index}", "cyan"))
|
||||||
|
if etype == EntryType.TOTP.value:
|
||||||
|
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
||||||
|
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f" Period: {entry.get('period', 30)}s Digits: {entry.get('digits', 6)}",
|
||||||
|
"cyan",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
notes = entry.get("notes", "")
|
||||||
|
if notes:
|
||||||
|
print(colored(f" Notes: {notes}", "cyan"))
|
||||||
|
elif etype == EntryType.SEED.value:
|
||||||
|
print(colored(" Type: Seed Phrase", "cyan"))
|
||||||
|
print(colored(f" Words: {entry.get('words', 24)}", "cyan"))
|
||||||
|
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||||
|
notes = entry.get("notes", "")
|
||||||
|
if notes:
|
||||||
|
print(colored(f" Notes: {notes}", "cyan"))
|
||||||
|
elif etype == EntryType.SSH.value:
|
||||||
|
print(colored(" Type: SSH Key", "cyan"))
|
||||||
|
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||||
|
notes = entry.get("notes", "")
|
||||||
|
if notes:
|
||||||
|
print(colored(f" Notes: {notes}", "cyan"))
|
||||||
|
elif etype == EntryType.PGP.value:
|
||||||
|
print(colored(" Type: PGP Key", "cyan"))
|
||||||
|
print(colored(f" Key Type: {entry.get('key_type', 'ed25519')}", "cyan"))
|
||||||
|
uid = entry.get("user_id", "")
|
||||||
|
if uid:
|
||||||
|
print(colored(f" User ID: {uid}", "cyan"))
|
||||||
|
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||||
|
notes = entry.get("notes", "")
|
||||||
|
if notes:
|
||||||
|
print(colored(f" Notes: {notes}", "cyan"))
|
||||||
|
elif etype == EntryType.NOSTR.value:
|
||||||
|
print(colored(" Type: Nostr Key", "cyan"))
|
||||||
|
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
||||||
|
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||||
|
notes = entry.get("notes", "")
|
||||||
|
if notes:
|
||||||
|
print(colored(f" Notes: {notes}", "cyan"))
|
||||||
|
else:
|
||||||
|
website = entry.get("website", "")
|
||||||
|
username = entry.get("username", "")
|
||||||
|
url = entry.get("url", "")
|
||||||
|
blacklisted = entry.get("blacklisted", False)
|
||||||
|
print(colored(f" Website: {website}", "cyan"))
|
||||||
|
print(colored(f" Username: {username or 'N/A'}", "cyan"))
|
||||||
|
print(colored(f" URL: {url or 'N/A'}", "cyan"))
|
||||||
|
print(colored(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "cyan"))
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
def handle_list_entries(self) -> None:
|
||||||
|
"""List entries and optionally show details."""
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
print("\nList Entries:")
|
||||||
|
print("1. All")
|
||||||
|
print("2. Passwords")
|
||||||
|
print("3. 2FA (TOTP)")
|
||||||
|
print("4. SSH Key")
|
||||||
|
print("5. Seed Phrase")
|
||||||
|
print("6. Nostr Key Pair")
|
||||||
|
print("7. PGP")
|
||||||
|
print("8. Back")
|
||||||
|
choice = input("Select entry type: ").strip()
|
||||||
|
if choice == "1":
|
||||||
|
filter_kind = None
|
||||||
|
elif choice == "2":
|
||||||
|
filter_kind = EntryType.PASSWORD.value
|
||||||
|
elif choice == "3":
|
||||||
|
filter_kind = EntryType.TOTP.value
|
||||||
|
elif choice == "4":
|
||||||
|
filter_kind = EntryType.SSH.value
|
||||||
|
elif choice == "5":
|
||||||
|
filter_kind = EntryType.SEED.value
|
||||||
|
elif choice == "6":
|
||||||
|
filter_kind = EntryType.NOSTR.value
|
||||||
|
elif choice == "7":
|
||||||
|
filter_kind = EntryType.PGP.value
|
||||||
|
elif choice == "8":
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print(colored("Invalid choice.", "red"))
|
||||||
|
continue
|
||||||
|
|
||||||
|
summaries = self.entry_manager.get_entry_summaries(filter_kind)
|
||||||
|
if not summaries:
|
||||||
|
continue
|
||||||
|
print(colored("\n[+] Entries:\n", "green"))
|
||||||
|
for idx, label in summaries:
|
||||||
|
print(colored(f"{idx}. {label}", "cyan"))
|
||||||
|
idx_input = input(
|
||||||
|
"Enter index to view details or press Enter to return: "
|
||||||
|
).strip()
|
||||||
|
if not idx_input:
|
||||||
|
return
|
||||||
|
if not idx_input.isdigit():
|
||||||
|
print(colored("Invalid index.", "red"))
|
||||||
|
continue
|
||||||
|
self.display_entry_details(int(idx_input))
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Failed to list entries: {e}", exc_info=True)
|
||||||
|
print(colored(f"Error: Failed to list entries: {e}", "red"))
|
||||||
|
|
||||||
def delete_entry(self) -> None:
|
def delete_entry(self) -> None:
|
||||||
"""Deletes an entry from the password index."""
|
"""Deletes an entry from the password index."""
|
||||||
try:
|
try:
|
||||||
|
Reference in New Issue
Block a user