The First Step
After much thought, doodling on paper, and some test code I came up with the following:
- a dictionary was all I need to hold all of the data
vault_context = \
{
"vfilename": "vault_file.xx", # vault file name
"vpassword": "crash bang", # vault encryption/decryption password
"vault" : {
"dbname" : "Password Vault",
"update" : "January 1, 1970",
"lastid" : 1,
"entries": [
{
"id" : 1,
"name" : "amazon.com",
"password": "goodboy",
"note" : None
}
] # end of entry list
} # end of vault
} # end of vault context
Note: at first I kept the context and vault separate, but it
turned out combining them made things simpler.
- in the "vault context" only the "vault" needs to be encrypted/decrypted.
- the "vault file name" and the "vault password" are temporary
and are lost when the program ends.
It is up to the users to remember them.
- each entry in the "vault entry list" is a small dictionary.
- the encryption/decryption process is:
- convert the "vault" dictionary to a JSON byte string.
- encrypt the JSON byte string using the "vault password".
- write the encrypted bytes to the "vault file name".
- to decrypt reverse the process.
- the "vault context" is passed to every function in the code
(it contains everything they need).
- At first the program is driven by a menu. The menu tests every function.
The menu program is then convert to use a GUI.
Note: I found Python encryption/decryption code on the web.
Three Useful Menu Functions
import user_interface as ui
# -------------------------------------------------------------------
# ---- 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 db name
# -------------------------------------------------------------------
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 action - 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
The Second step
Code the design
More Details
All searches use sub-string matches, and may return more that one entry.
The way to insure only one entry is returned is to do an ID search.
A unique ID (integer) is assigned to each entity when it is created.
IDs start at 1 and incremented +1 each time.
Deleting entries will leave gaps in the range of IDs.
They are no longer contiguous. This is not
a problem, but just in case, a function is provided to renumber entries
starting at 1. This will make the IDs contiguous.
FYI:
How to Encrypt JSON in Python