solution_118_03.py

#!/usr/bin/python3
# ====================================================================
# password vault 03 - menus
# ====================================================================

import sys
import copy
import user_interface          as ui    # uer interface
import password_vault_04_utils as utl   # utility functions
import password_vault_02_ed    as ed    # JSON encrypt/decrypt


# ---- the "vault" is the only data encrypted and stored on disk
# ---- the "filename", "password" are in memory and are lost when
# ---- the program ends 

default_vault_context = \
    {
    "vfilename": None,     # vault file name
    "vpassword": None,     # vault password
    "vault"    : {
                 "dbname" : "Default Password Vault",
                 "update" : "January 1, 1970",
                 "lastid" : 0,
                 "entries": []
                 }
    }

test_vault_context = \
    {
    "vfilename": None,
    "vpassword": None,
    "vault"    : {
                 "dbname" : "test Password Vault",
                 "update" : "January 1, 1970",
                 "lastid" : 6,
                 "entries": [
                     {
                     "id"      : 1,
                     "name"    : "amazon.com",
                     "password": "goodboy",
                     "note"    : None
                     },
                     {
                     "id"      : 2,
                     "name"    : "AMAZON.COM",
                     "password": "badboy",
                     "note"    : "refurbished computers"
                     },
                     {
                     "id"      : 3,
                     "name"    : "bestbuy.com",
                     "password": "72zz93#",
                     },
                     {
                     "id"      : 5,
                     "name"    : "www.adafruit.com",
                     "password": "AD8##sx",
                     "note"    : "the good place, lots of DIY projects"
                     },
                     {
                     "id"      : 6,
                     "name"    : "www.chase.com",
                     "password": "m0M9m8##",
                     "note"    : ""
                     }
                            ]    # end of entry list
                 }               # end of vault
    }                            # end of vault info

# -------------------------------------------------------------------
# ---- query yes/no
# -------------------------------------------------------------------

def query_yes_no(p=' [yY]: '):

    ans = ui.get_user_input(p)

    if not ans:
        return False
        
    if ans[0] == 'y' or ans[0] == 'Y':
        return True 

    return False

# -------------------------------------------------------------------
# ---- get vault password
# -------------------------------------------------------------------

def get_vault_password(vault_context):

    pw = vault_context["vpassword"]
    
    print()
    print(f'The currrent vault\'s pasword is ({pw})')
    tf = query_yes_no('Use this password [yY]? ')
    if tf:
        return None

    pw = ui.get_user_input('Enter a password: ')

    if not pw:
        return None
        
    return pw

# -------------------------------------------------------------------
# ---- get vault file name
# -------------------------------------------------------------------

def get_vault_filename(vault_context):

    fn = vault_context["vfilename"]
    
    print()
    print(f'The currrent vault\'s file name is ({fn})')
    tf = query_yes_no('Use this filename [yY]? ')
    if tf:
        return None

    fn = ui.get_user_input('Enter a file name: ')

    if not fn:
        return None
        
    return fn
    

# -------------------------------------------------------------------
# ---- get vault dbname
# -------------------------------------------------------------------

def get_vault_dbname(vault_context):

    db = vault_context["vault"]["dbname"]
    
    print()
    print(f'The currrent vault\'s db name is ({db})')
    tf = query_yes_no('Use this db name [yY]? ')
    if tf:
        return None

    db = ui.get_user_input('Enter a db name: ')

    if not db:
        return None
        
    return db

# -------------------------------------------------------------------
# ---- menu - change vault password
# -------------------------------------------------------------------

def change_vault_password_action(vault_context):

    pw = get_vault_password(vault_context)
    
    if not pw:
        print()
        print('no password entered - no action taken')
        return

    vault_context["vpassword"] = pw

    print()
    print(f'vault\'s password changed to ({pw})')
    return

# -------------------------------------------------------------------
# ---- menu - change vault file name
# -------------------------------------------------------------------

def change_vault_filename_action(vault_context):

    fn = get_vault_filename(vault_context)
    
    if not fn:
        print()
        print('no file name entered - no action taken')
        return

    vault_context["vfilename"] = fn

    print()
    print(f'vault\'s file name changed to ({fn})')
    return


# -------------------------------------------------------------------
# ---- menu - change vault db name
# -------------------------------------------------------------------

def change_vault_dbname_action(vault_context):

    db = get_vault_dbname(vault_context)
    
    if not db:
        print()
        print('no db name entered - no action taken')
        return

    vault_context["vault"]["dbname"] = db

    print()
    print(f'vault\'s db name changed to ({db})')
    return

# -------------------------------------------------------------------
# ---- menu - load a vault
# -------------------------------------------------------------------

def load_vault_action(vault_context):

    # ---- get vault's file name
        
    print()
    fn = ui.get_user_input('Enter vault\'s file name: ') 

    if not fn:
        print()
        print('no file name entered - no action taken')
        return None

    # ---- get vault's password
        
    print()
    pw = ui.get_user_input('Enter vault\'s password: ') 

    if not pw:
        print()
        print('no password entered - no action taken')
        return None

    try:

        new_vault = ed.decrypt_dictionary(pw,fn)

    ##except BaseException as ex:        
    ##    print(f'Exception Name: {type(ex).__name__}')
    ##    print(f'Exception Desc: {ex}')
    ##    err = True

    except BaseException as ex:
        print('Decryption error')
        print(f'Exception Name: {type(ex).__name__}')
        print('probably a bad password')
        return
        
    vault_context["vpassword"]       = pw
    vault_context["vfilename"]       = fn
    vault_context["vault"]           = new_vault

    print()
    print('A new vault is now in memory - ' +
          'The old vault was replaced')

    return
    
# -------------------------------------------------------------------
# ---- menu - save the current vault encrypted to a file
# -------------------------------------------------------------------

def save_vault_action(vault_context):

    vault_stats_action(vault_context)

    print()
    print('These are the current vault\'s information')
    print('Are they the values you want?')
    tf = query_yes_no('Proceed with saving [yY]? ')
    if not tf:
        print()
        print('change the vault\'s information - then try again') 
        return None

    pw = vault_context["vpassword"]
    fn = vault_context["vfilename"]
    db = vault_context["vault"]["dbname"]

    if not pw or not fn or not db:
        print()
        print('Bad vault information - no action taken')
        return

    vault_context["vault"]["update"] = utl.current_date()

    ed.encrypt_dictionary(vault_context["vault"],pw,fn)


    print()
    print(f'vault written to file ({fn})')
    
    return 

# -------------------------------------------------------------------
# ---- create a new vault
# -------------------------------------------------------------------

def create_new_vault_action(vault_context):

    vault = vault_context["vault"]
    
    print()
    print(f'The current vault in memory is ({vault["dbname"]})')
    print('Creating a new vault will delete any existing data')
    print('in the current vault unless it has been saved.')
    print()
    if not query_yes_no('create a new vault [yY]: '):
        print()
        print('no action taken')
        return None

    vault_context["vault"] = copy.deepcopy(default_vault_context['vault'])

    vault_context["vfilename"]       = None
    vault_context["vpassword"]       = None
    vault_context["vault"]["dbname"] = None
    vault_context["vault"]["update"] = utl.current_date()

    print()
    print('A new vault is now in memory - ' +
          'The old vault was replaced')
    print('Please update the new vault\'s information')
    return       

# -------------------------------------------------------------------
# ---- menu - display the current vault's stats
# -------------------------------------------------------------------

def vault_stats_action(vault_context,all=False):

    print()
    print('----stats----------------------------------')
    print(f'vfilename: {vault_context["vfilename"]}')
    print(f'vpassword: {vault_context["vpassword"]}')
    if vault_context["vault"] is None:
        print(f'vault   : {vault_context["vault"]}')
    else:
        print(f'dbname   : {vault_context["vault"]["dbname"]}')
        print(f'update   : {vault_context["vault"]["update"]}')
        print(f'lastid   : {vault_context["vault"]["lastid"]}')
        print(f'entries  : {len(vault_context["vault"]["entries"])}')
        if all:
            for e in vault_context["vault"]["entries"]:
                print(f'ID = {e["id"]:<3}  name = {e["name"]}')

    print('-------------------------------------------')
    return

# -------------------------------------------------------------------
# ---- password vault help
# -------------------------------------------------------------------

def vault_help():
    print()
    utl.display_message(0)
    print()
    utl.display_message(2)
    print()
    utl.display_message(3)
    print()
    utl.display_message(4)

# -------------------------------------------------------------------
# ---- menu - main
# -------------------------------------------------------------------

def main_menu(vault_context):

    if not ui.running_python3():
        print()
        print('Not running Python3 - exit program')
        sys.exit()

    while(True):
        
        ####ui.clear_screen()

        dbname = vault_context["vault"]["dbname"]
        
        print('  ==================================================')
        print(f'  {utl.return_message(2)}  ({dbname})')
        print('  ==================================================')
        print('  ##  Action')
        print('  --  ----------------------------------------------')
        print('   0  exit program')
        print('   1  search primary name')
        print('   2  search notes')
        print('   3  search for ID')
        print('   4  add entry')
        print('   5  delete entry')
        print('   6  modify entry')
        print('  10  load a new vault')
        print('  11  save current vault')
        print('  12  create a new vault')
        print('  13  output CSV file')
        print('  14  vault stats')
        print('  15  vault stats (all)')
        print('  16  change vault\'s password')
        print('  17  change vault\'s file name')
        print('  18  change vault\'s db name')
        print('  88  renumber IDs')
        print('  99  help')

        # --- ask the user to select an action

        print()
        sel = ui.get_user_input('  Enter selection: ') 

        if not sel:            # no selection?
            continue
            
        # --- valid selection?
        
        tf,n = ui.is_int(sel)
        if tf == False:
            print()
            print(f'  Bad selection entered ({sel})')
            ui.pause()
            continue
            
        # --- take action
         
        if n == 0:
            break
        
        elif n == 1:
            pass
        
        elif n == 2:
            pass
        
        elif n == 3:
            pass
        
        elif n == 4:
            pass
        
        elif n == 5:
            pass
        
        elif n == 10:
            load_vault_action(vault_context)
                 
        elif n == 11:
            save_vault_action(vault_context)
        
        elif n == 12:
            create_new_vault_action(vault_context)

        elif n == 13:
            pass
        
        elif n == 14:
            vault_stats_action(vault_context)

        elif n == 15:
            vault_stats_action(vault_context,True)

        elif n == 16:
            change_vault_password_action(vault_context)

        elif n == 17:
            change_vault_filename_action(vault_context)
            
        elif n == 18:
            change_vault_dbname_action(vault_context)

        elif n == 88:
            utl.renumber_vault_ids(vault_context)

        elif n == 99:
            ####ui.clear_screen()
            vault_help()
        
        else:
           print()
           print(f'  Bad selection entered ({n})')
           
        ui.pause()

# -------------------------------------------------------------------
# ---- main - use the test vault info
# -------------------------------------------------------------------

if __name__ == '__main__':

    main_menu(test_vault_context)