Skip to content

Commit 9bd9786

Browse files
committed
feat: add rich for improved styling
1 parent 2598d25 commit 9bd9786

File tree

2 files changed

+138
-56
lines changed

2 files changed

+138
-56
lines changed

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
bcrypt
2+
cryptography
3+
rich

src/main.py

Lines changed: 135 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,31 @@
88
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
99
from cryptography.hazmat.primitives import hashes
1010
import base64
11+
from rich.console import Console
12+
from rich.table import Table
13+
from rich.text import Text
14+
from rich.align import Align
15+
import sys
1116

1217
# To-do
13-
# Change to simple command parsing (using argparse or click)
18+
# Add ability to go back at anytime
1419
# Category / Tags
1520
# Export / import functionality
16-
# Web UI with Flask
1721

1822
class PasswordManager:
1923
def __init__(self):
24+
self.console = Console()
2025
self.master_hash = None
2126
self.is_authenticated = False
2227
self.fernet = None
23-
self.DB_PATH = os.path.join(os.path.dirname(__file__), "vault.db")
28+
29+
if getattr(sys, 'frozen', False):
30+
# Running as compiled executable
31+
self.DB_PATH = os.path.join(os.path.dirname(sys.executable), "vault.db")
32+
else:
33+
# Running as Python script
34+
self.DB_PATH = os.path.join(os.path.dirname(__file__), "vault.db")
35+
2436
self._initialize_database()
2537

2638
def _initialize_database(self):
@@ -129,7 +141,7 @@ def _get_user_password(self):
129141
prompt = "Create password: " if self._check_master_password_exists() else "Create master password: "
130142

131143
while True:
132-
password = input(prompt).strip()
144+
password = self.console.input(f"[yellow]> [/yellow]{prompt}").strip()
133145
if self._check_complexity(password):
134146
print("Password created successfully.")
135147
return password
@@ -215,7 +227,7 @@ def _decrypt_password(self, encrypted_password_b64):
215227
def authenticate(self):
216228
"""Prompt for master password until correct"""
217229
while not self.is_authenticated:
218-
attempt = input("Enter master password: ").strip()
230+
attempt = self.console.input("[yellow]> [/yellow]Enter master password: ").strip()
219231

220232
if self._verify_master_password(attempt):
221233
print("Master password correct")
@@ -230,7 +242,7 @@ def authenticate(self):
230242
def create_master_password(self):
231243
"""Create or generate a new master password"""
232244
while True:
233-
choice = input("Would you like to create your own master password or have us create one for you? (create/generate): ").strip()
245+
choice = self.console.input("[yellow]> [/yellow]Would you like to create your own master password or have us create one for you? (create/generate): ").strip()
234246

235247
if choice == "create":
236248
password = self._get_user_password()
@@ -241,7 +253,7 @@ def create_master_password(self):
241253
print("Store this securely - you won't see it again!\n")
242254
break
243255
else:
244-
print("Invalid input. Enter 'create' or 'generate'.")
256+
self.console.print("Invalid input. Enter 'create' or 'generate'", style="red")
245257

246258
# Store the master password hash and salt
247259
self.master_hash= self._hash_master_password(password)
@@ -267,15 +279,22 @@ def _handle_view(self):
267279
entries = cursor.fetchall()
268280

269281
if entries:
270-
print("Stored password entries:")
271-
print("-" * 50)
282+
table = Table(title="\nStored password entries")
283+
table.add_column("ID", justify="center", style="cyan", no_wrap=True)
284+
table.add_column("Website", justify="center", style="cyan", no_wrap=True)
285+
table.add_column("Username", justify="center", style="cyan", no_wrap=True)
286+
table.add_column("Creation Date", justify="center", style="cyan", no_wrap=True)
287+
272288
for entry in entries:
273-
print(f"ID: {entry[0]} | Website: {entry[1]} | Username: {entry[2]} | Created: {entry[3]}")
289+
table.add_row(f"{entry[0]}", f"{entry[1]}", f"{entry[2]}", f"{entry[3]}")
274290

275-
print("-" * 50)
276-
selected_id = input("Enter the ID of the password you want to view: ").strip()
291+
self.console.print(table)
292+
print("")
293+
294+
selected_id = self.console.input("[yellow]> [/yellow]Enter the ID of the password you want to view: ").strip()
277295

278296
if not self._validate_input(selected_id, "ID"):
297+
print("")
279298
return
280299

281300
cursor.execute("SELECT password FROM passwords WHERE id = ?", (selected_id,))
@@ -284,16 +303,16 @@ def _handle_view(self):
284303
if result:
285304
decrypted = self._decrypt_password(result[0])
286305
if decrypted:
287-
print(f"Password for entry ID {selected_id}: {decrypted}")
306+
print(f"Password for entry ID {selected_id}: {decrypted}\n")
288307
else:
289-
print("Failed to decrypt password.")
308+
print("Failed to decrypt password.\n")
290309
else:
291-
print("No password found for that ID.")
310+
print("No password found for that ID.\n")
292311
else:
293-
print("No passwords stored yet.")
312+
print("No passwords stored yet.\n")
294313

295314
except sqlite3.Error as e:
296-
print(f"Database error: {e}")
315+
print(f"Database error: {e}\n")
297316

298317
def _handle_add(self):
299318
"""Add a password to the database"""
@@ -303,17 +322,17 @@ def _handle_add(self):
303322
try:
304323
# Validate inputs
305324
while True:
306-
website = input("Enter website: ").strip()
325+
website = self.console.input("[yellow]> [/yellow]Enter website: ").strip()
307326
if self._validate_input(website, "Website"):
308327
break
309328

310329
while True:
311-
username = input("Enter username: ").strip()
330+
username = self.console.input("[yellow]> [/yellow]Enter username: ").strip()
312331
if self._validate_input(username, "Username"):
313332
break
314333

315334
while True:
316-
choice = input("Would you like to create your own password or have us create one for you? (create/generate): ").strip()
335+
choice = self.console.input("[yellow]> [/yellow]Would you like to create your own password or have us create one for you? (create/generate): ").strip()
317336

318337
if choice == "create":
319338
password = self._get_user_password()
@@ -337,10 +356,10 @@ def _handle_add(self):
337356
(website, username, encrypted_password))
338357

339358
connection.commit()
340-
print("Password added successfully")
359+
print("Password added successfully.\n")
341360

342361
except sqlite3.Error as e:
343-
print(f"Database error: {e}")
362+
print(f"Database error: {e}\n")
344363

345364
def _handle_update(self):
346365
"""Update a password from the database"""
@@ -352,13 +371,19 @@ def _handle_update(self):
352371
entries = cursor.fetchall()
353372

354373
if entries:
355-
print("Stored password entries:")
356-
print("-" * 50)
374+
table = Table(title="\nStored password entries")
375+
table.add_column("ID", justify="center", style="cyan", no_wrap=True)
376+
table.add_column("Website", justify="center", style="cyan", no_wrap=True)
377+
table.add_column("Username", justify="center", style="cyan", no_wrap=True)
378+
table.add_column("Creation Date", justify="center", style="cyan", no_wrap=True)
379+
357380
for entry in entries:
358-
print(f"ID: {entry[0]} | Website: {entry[1]} | Username: {entry[2]} | Created: {entry[3]}")
381+
table.add_row(f"{entry[0]}", f"{entry[1]}", f"{entry[2]}", f"{entry[3]}")
359382

360-
print("-" * 50)
361-
selected_id = input("Enter the ID of the password you want to update: ").strip()
383+
self.console.print(table)
384+
print("")
385+
386+
selected_id = self.console.input("[yellow]> [/yellow]Enter the ID of the password you want to update: ").strip()
362387

363388
if not self._validate_input(selected_id, "ID"):
364389
pass
@@ -370,10 +395,10 @@ def _handle_update(self):
370395
website_name = website_result[0]
371396

372397
while True:
373-
username_update_decision = input("Do you want to update the username? (yes/no): ").strip()
398+
username_update_decision = self.console.input("[yellow]> [/yellow]Do you want to update the username? (yes/no): ").strip()
374399
if username_update_decision == "yes":
375400
while True:
376-
new_username = input(f"Enter new username for {website_name}: ").strip()
401+
new_username = self.console.input(f"[yellow]> [/yellow]Enter new username for {website_name}: ").strip()
377402
if self._validate_input(new_username, "Username"):
378403
cursor.execute("UPDATE passwords SET username = ? WHERE id = ?", (new_username, selected_id,))
379404
connection.commit()
@@ -387,10 +412,10 @@ def _handle_update(self):
387412
continue
388413

389414
while True:
390-
password_update_decision = input("Do you want to update the password? (yes/no): ").strip()
415+
password_update_decision = self.console.input("[yellow]> [/yellow]Do you want to update the password? (yes/no): ").strip()
391416
if password_update_decision == "yes":
392417
while True:
393-
choice = input("Would you like to create your own password or have us create one for you? Enter 'create' or 'generate': ").strip()
418+
choice = self.console.input("[yellow]> [/yellow]Would you like to create your own password or have us create one for you? Enter (create/generate): ").strip()
394419

395420
if choice == "create":
396421
new_password = self._get_user_password()
@@ -408,19 +433,22 @@ def _handle_update(self):
408433
connection.commit()
409434
print("Password updated successfully.")
410435
else:
411-
print("Failed to encrypt password. Password not updated.")
436+
print("Failed to encrypt password. Password not updated.\n")
412437
break
413438
elif password_update_decision == "no":
414439
break
415440
else:
416441
print("Invalid input. Enter 'yes' or 'no' to update password.")
417442
continue
443+
444+
print(f"Update for ID {selected_id} complete.\n")
445+
418446
else:
419-
print("No password found for that ID.")
447+
print("No password found for that ID.\n")
420448
else:
421-
print("No passwords stored yet.")
449+
print("No passwords stored yet.\n")
422450
except sqlite3.Error as e:
423-
print(f"Database error: {e}")
451+
print(f"Database error: {e}\n")
424452

425453
def _handle_delete(self):
426454
"""Delete a password from the database"""
@@ -432,23 +460,28 @@ def _handle_delete(self):
432460
entries = cursor.fetchall()
433461

434462
if entries:
435-
print("Stored password entries:")
436-
print("-" * 50)
463+
table = Table(title="\nStored password entries")
464+
table.add_column("ID", justify="center", style="cyan", no_wrap=True)
465+
table.add_column("Website", justify="center", style="cyan", no_wrap=True)
466+
table.add_column("Username", justify="center", style="cyan", no_wrap=True)
467+
table.add_column("Creation Date", justify="center", style="cyan", no_wrap=True)
468+
437469
for entry in entries:
438-
print(f"ID: {entry[0]} | Website: {entry[1]} | Username: {entry[2]} | Created: {entry[3]}")
470+
table.add_row(f"{entry[0]}", f"{entry[1]}", f"{entry[2]}", f"{entry[3]}")
439471

440-
print("-" * 50)
472+
self.console.print(table)
473+
print("")
441474

442475
while True:
443476

444-
selected_id = input("Enter the ID of the password you want to delete: ").strip()
477+
selected_id = self.console.input("[yellow]> [/yellow]Enter the ID of the password you want to delete: ").strip()
445478

446479
if not self._validate_input(selected_id, "ID"):
447480
continue
448481

449-
delete_confirmation = input(f"Are you sure you want to delete ID: {selected_id}? (yes/no): ").strip()
482+
delete_confirmation = self.console.input(f"[yellow]> [/yellow][red]Are you sure you want to delete ID: {selected_id}? (Y/n): [/red]").strip()
450483

451-
if delete_confirmation == "yes":
484+
if delete_confirmation == "Y":
452485
cursor.execute("SELECT website FROM passwords WHERE id = ?", (selected_id,))
453486
website_result = cursor.fetchone()
454487

@@ -457,22 +490,63 @@ def _handle_delete(self):
457490
# Proceed to delete after confirming it exists
458491
cursor.execute("DELETE FROM passwords WHERE id = ?", (selected_id,))
459492
connection.commit()
460-
print(f"Password for website '{website_name}' (ID: {selected_id}) has been deleted.")
493+
print(f"Password for website '{website_name}' (ID: {selected_id}) has been deleted.\n")
461494
else:
462-
print("No password found for that ID.")
495+
print("No password found for that ID.\n")
463496
break
464-
elif delete_confirmation == "no":
497+
elif delete_confirmation == "n":
465498
break
466499
else:
467500
print("Invalid input. 'yes' or 'no' for password deletion.")
468501
else:
469-
print("No passwords stored yet.")
502+
print("No passwords stored yet.\n")
470503
except sqlite3.Error as e:
471-
print(f"Database error: {e}")
504+
print(f"Database error: {e}\n")
505+
506+
def startup_text(self):
507+
console = Console()
508+
console.clear()
509+
510+
header = Text()
511+
header.append("vanta", style="bold white")
512+
header.append("\n")
513+
header.append("v0.1.0", style="dim white")
514+
515+
console.print()
516+
console.print(Align.center(header))
517+
console.print()
518+
519+
self.show_help()
520+
521+
def show_help(self):
522+
console = Console()
523+
524+
table = Table(show_header=False, show_lines=False, box=None, padding=(0, 2))
525+
table.add_column("Command", style="dim white", width=17)
526+
table.add_column("Description", style="white", width=20)
527+
table.add_column("Shortcut", style="dim white", width=10)
528+
529+
commands = [
530+
("/help", "show help", "/h"),
531+
("/view", "view passwords", "/v"),
532+
("/add", "add password", "/a"),
533+
("/update", "update password", "/u"),
534+
("/delete", "delete password", "/d"),
535+
("/quit", "quit program", "/q"),
536+
]
537+
538+
for cmd, desc, shortcut in commands:
539+
table.add_row(cmd, desc, shortcut)
540+
541+
print("")
542+
console.print(Align.center(table))
543+
console.print()
472544

473545
def run(self):
474546
"""Main application loop"""
475-
print("Welcome to Vanta Password Manager\n")
547+
console = Console()
548+
549+
console.print("Welcome to Vanta Password Manager", style="green")
476550

477551
# Check if master password already exists
478552
if self._check_master_password_exists():
@@ -482,29 +556,34 @@ def run(self):
482556
self.create_master_password()
483557

484558
if self.is_authenticated:
559+
console.clear()
560+
self.startup_text()
485561
print("Access granted to password database!")
486562

487563
while True:
488-
manager_process = input("\nDo you want to view, add, delete, update a password, or quit? (view/add/update/delete/quit): ").strip()
564+
manager_process = console.input("[yellow]> [/yellow]").strip()
489565

490566
# Select and then view a password from the database
491-
if manager_process == "view":
567+
if manager_process == "/help" or manager_process == "/h":
568+
self.show_help()
569+
570+
elif manager_process == "/view" or manager_process == "/v":
492571
self._handle_view()
493572

494-
elif manager_process == "add":
573+
elif manager_process == "/add" or manager_process == "/a":
495574
self._handle_add()
496575

497-
elif manager_process == "update":
576+
elif manager_process == "/update" or manager_process == "/u":
498577
self._handle_update()
499578

500-
elif manager_process == "delete":
579+
elif manager_process == "/delete" or manager_process == "/d":
501580
self._handle_delete()
502581

503-
elif manager_process == "quit":
504-
print("Goodbye!")
582+
elif manager_process == "/quit" or manager_process == "/q":
583+
print("Goodbye, friend.")
505584
break
506585
else:
507-
print("Choose 'view', 'add', 'delete', 'update', or 'quit'")
586+
pass
508587

509588
def main():
510589
manager = PasswordManager()

0 commit comments

Comments
 (0)