88from cryptography .hazmat .primitives .kdf .pbkdf2 import PBKDF2HMAC
99from cryptography .hazmat .primitives import hashes
1010import 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
1822class 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 = "\n Stored 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 = "\n Stored 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 = "\n Stored 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 ("\n Do 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
509588def main ():
510589 manager = PasswordManager ()
0 commit comments