1313from rich .text import Text
1414from rich .align import Align
1515import platform
16+ import pyperclip
1617
1718# To-do
1819# Add ability to go back at anytime
2122
2223class PasswordManager :
2324 def __init__ (self ):
24- self .version = "0.2 .0"
25+ self .version = "0.3 .0"
2526 self .console = Console ()
2627 self .master_hash = None
2728 self .is_authenticated = False
2829 self .fernet = None
2930 self .DB_PATH = self ._get_database_path ()
31+ self .most_recent_id = ""
3032
3133 self ._initialize_database ()
3234
@@ -195,7 +197,6 @@ def _get_user_password(self):
195197 while True :
196198 password = self .console .input (f"[yellow]> [/yellow]{ prompt } " ).strip ()
197199 if self ._check_complexity (password ):
198- print ("Password created successfully." )
199200 return password
200201
201202 def _hash_master_password (self , password ):
@@ -366,6 +367,7 @@ def _handle_view(self):
366367 decrypted = self ._decrypt_password (result [0 ])
367368 if decrypted :
368369 print (f"Password for entry ID { selected_id } : { decrypted } \n " )
370+ self .most_recent_id = selected_id
369371 else :
370372 print ("Failed to decrypt password.\n " )
371373 else :
@@ -424,6 +426,8 @@ def _handle_add(self):
424426 VALUES (?, ?, ?)''' ,
425427 (website , username , encrypted_password ))
426428
429+ self .most_recent_id = cursor .lastrowid
430+
427431 connection .commit ()
428432 print ("Password added successfully.\n " )
429433
@@ -518,6 +522,7 @@ def _handle_update(self):
518522 continue
519523
520524 print (f"Update for ID { selected_id } complete.\n " )
525+ self .most_recent_id = selected_id
521526
522527 else :
523528 print ("No password found for that ID.\n " )
@@ -566,10 +571,38 @@ def _handle_master(self):
566571 print (f"Failed to re-encrypt password for ID { pw_id } . Data might be inconsistent." )
567572
568573 connection .commit ()
574+
569575 except sqlite3 .Error as e :
570576 print (f"Database error: { e } \n " )
571577
578+ def _handle_copy (self ):
579+ """Copy most recent password to clipboard"""
580+ with sqlite3 .connect (self .DB_PATH ) as connection :
581+ cursor = connection .cursor ()
582+ selected_id = self .most_recent_id
572583
584+ try :
585+ cursor .execute ("SELECT id, website, username, created_at FROM passwords ORDER BY created_at DESC" )
586+ entries = cursor .fetchall ()
587+
588+ if entries :
589+ cursor .execute ("SELECT password FROM passwords WHERE id = ?" , (selected_id ,))
590+ result = cursor .fetchone ()
591+
592+ if result :
593+ decrypted = self ._decrypt_password (result [0 ])
594+ if decrypted :
595+ pyperclip .copy (decrypted )
596+ print ("Most recent password copied to clipboard.\n " )
597+ else :
598+ print ("Failed to decrypt password.\n " )
599+ else :
600+ print ("Failed to copy most recent password.\n " )
601+ else :
602+ print ("No passwords stored yet.\n " )
603+
604+ except sqlite3 .Error as e :
605+ print (f"Database error: { e } \n " )
573606
574607 def _handle_delete (self ):
575608 """Delete a password from the database"""
@@ -637,23 +670,43 @@ def startup_text(self):
637670 console .print (Align .center (header ))
638671 console .print ()
639672
640- self .show_help ()
673+ table = Table (show_header = False , show_lines = False , box = None , padding = (0 , 2 ))
674+ table .add_column ("Command" , style = "dim white" , width = 17 )
675+ table .add_column ("Description" , style = "white" , width = 20 )
676+ table .add_column ("Shortcut" , style = "dim white" , width = 10 )
677+
678+ commands = [
679+ ("/help" , "all commands" , "/h" ),
680+ ("/view" , "view passwords" , "/v" ),
681+ ("/add" , "add password" , "/a" ),
682+ ("/update" , "update password" , "/u" ),
683+ ("/delete" , "delete password" , "/d" ),
684+ ("/quit" , "quit program" , "/q" ),
685+ ]
686+
687+ for cmd , desc , shortcut in commands :
688+ table .add_row (cmd , desc , shortcut )
689+
690+ print ("" )
691+ console .print (Align .center (table ))
692+ console .print ()
641693
642694 def show_help (self ):
643695 console = Console ()
644696
645697 table = Table (show_header = False , show_lines = False , box = None , padding = (0 , 2 ))
646- table .add_column ("Command" , style = "dim white" , width = 17 )
698+ table .add_column ("Command" , style = "dim white" , width = 10 )
647699 table .add_column ("Description" , style = "white" , width = 20 )
648700 table .add_column ("Shortcut" , style = "dim white" , width = 10 )
649701
650702 commands = [
651- ("/help" , "show help " , "/h" ),
703+ ("/help" , "all commands " , "/h" ),
652704 ("/info" , "version details" , "/i" ),
653705 ("/view" , "view passwords" , "/v" ),
654706 ("/add" , "add password" , "/a" ),
655707 ("/update" , "update password" , "/u" ),
656708 ("/delete" , "delete password" , "/d" ),
709+ ("/copy" , "copy to clipboard" , "/c" ),
657710 ("/master" , "change master" , "/m" ),
658711 ("/quit" , "quit program" , "/q" ),
659712 ]
@@ -662,7 +715,7 @@ def show_help(self):
662715 table .add_row (cmd , desc , shortcut )
663716
664717 print ("" )
665- console .print (Align .center (table ))
718+ console .print (Align .left (table ))
666719 console .print ()
667720
668721 def show_info (self ):
@@ -718,6 +771,9 @@ def run(self):
718771 elif manager_process == "/delete" or manager_process == "/d" :
719772 self ._handle_delete ()
720773
774+ elif manager_process == "/copy" or manager_process == "/c" :
775+ self ._handle_copy ()
776+
721777 elif manager_process == "/master" or manager_process == "/m" :
722778 self ._handle_master ()
723779
0 commit comments