197 lines
6.4 KiB
Python
197 lines
6.4 KiB
Python
"""
|
|
Peek:
|
|
Operate on a list of dicts in the cmdline and store them (optionally encrypted) on the filesystem
|
|
I AM NOT A SECURITY EXPERT. NO GUARANTEE. USE AT YOUR OWN RISK
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import base64
|
|
from encrypt_json import load_obj, save_obj
|
|
|
|
def base64_encode(objs) -> str:
|
|
# Storing in the illegible base64 format instead of clear text creates
|
|
# A FALSE SENSE OF DATA SECURITY, SHOULD BE DISABlED
|
|
|
|
# In Python, bytes objects are immutable sequences of single bytes
|
|
# string <-e/d-> bytes <--base64 e/d--> bytes <-e/d-> string
|
|
s = json.dumps(objs)
|
|
return base64.encodebytes(s.encode('utf-8')).decode('utf-8')
|
|
|
|
def base64_decode(s:str):
|
|
s = base64.decodebytes(s.encode('utf-8')).decode('utf-8')
|
|
return json.loads(s)
|
|
|
|
def printraw(mem):
|
|
for i,obj in enumerate(mem):
|
|
print(i)
|
|
for k,v in obj.items():
|
|
print(k+":"+v,end='\t')
|
|
print('')
|
|
|
|
def printtable(table):
|
|
# Prints a table in markdown style
|
|
table.insert(1,["---"]*len(table[0]))
|
|
for row in table:
|
|
print("|" + "|".join(row) + "|")
|
|
|
|
def printfmt(mem, keys, shortcuts):
|
|
# Parse which keys are required
|
|
key_order = list()
|
|
list_others = False
|
|
keys = keys.split()
|
|
for c in keys[0]:
|
|
if c == '+':
|
|
list_others = True
|
|
elif c in shortcuts.keys():
|
|
key_order.append(shortcuts[c])
|
|
else:
|
|
print("unknown character shortcut '%s', cannot print formatted"%c)
|
|
return
|
|
key_order.extend(keys[1:])
|
|
# List dicts that has the required keys
|
|
table = list()
|
|
table.append(["#"] + key_order + (["+"] if list_others else []))
|
|
for i,obj in enumerate(mem):
|
|
if all([key in obj for key in key_order]):
|
|
row = [str(i)]
|
|
for key in key_order:
|
|
row.append(obj[key])
|
|
if list_others:
|
|
s = ""
|
|
for k,v in obj.items():
|
|
if k not in key_order:
|
|
s += k+":"+v+";"
|
|
row.append(s)
|
|
table.append(row)
|
|
printtable(table)
|
|
|
|
def inputkv(obj, shortcuts):
|
|
while True:
|
|
k = input("k:")
|
|
if k=='':
|
|
break # end of input
|
|
if len(k)==1:
|
|
try:
|
|
k = shortcuts[k]
|
|
except KeyError:
|
|
print("unknown character shortcut %s, one-character key is reserved for shortcuts, \
|
|
choose another key"%k)
|
|
continue # invalid input
|
|
if k in obj:
|
|
print("rewriting key %s, original value is "%k + obj[k])
|
|
v = input("v:")
|
|
obj[k] = v
|
|
|
|
def inputshortcuts():
|
|
sc = {}
|
|
while True:
|
|
k = input("k:")
|
|
if k=='':
|
|
break
|
|
if len(k)!=1:
|
|
print("shortcut must be one-character")
|
|
continue
|
|
v = input("v:")
|
|
sc[k] = v
|
|
return sc
|
|
|
|
def loaddb(db_name, encode_method, password):
|
|
if encode_method=="base64":
|
|
try:
|
|
f = open(db_name+".store",'r')
|
|
obj = base64_decode(f.read())
|
|
f.close()
|
|
except Exception as e:
|
|
print(type(e).__name__ + ":" + str(e))
|
|
print("Corrupted save file")
|
|
exit(-1)
|
|
elif encode_method=="fernet":
|
|
f = open(db_name+".json",'r')
|
|
d=f.read(); f.close()
|
|
obj = load_obj(d, password)
|
|
else:
|
|
print("unsupported encode method, exiting")
|
|
exit(-1)
|
|
return obj
|
|
|
|
def savedb(db_name, encode_method, obj, password):
|
|
if encode_method=="base64":
|
|
f = open(db_name+".store",'w')
|
|
f.write(base64_encode(obj))
|
|
elif encode_method=="fernet":
|
|
f = open(db_name+".json",'w')
|
|
salt = os.urandom(16)
|
|
d = save_obj(obj, password, salt)
|
|
f.write(d)
|
|
else:
|
|
print("unsupported encode method, exiting")
|
|
f.close()
|
|
|
|
def interact(db_name, decode_method, encode_method, password, create):
|
|
db_obj = {"mem":[], "shortcuts":{}} if create else loaddb(db_name, decode_method, password)
|
|
if type(db_obj)==list:
|
|
db_obj = {"mem":db_obj, "shortcuts":{}}
|
|
mem, shortcuts = db_obj["mem"], db_obj["shortcuts"]
|
|
# Main loop
|
|
help_msg = "p: Print Raw, f: Print Formatted, a: Add, m: Modify, d: Delete,\
|
|
wq: Save & Quit, q!: Don't Save, sc: define shortcuts"
|
|
print(help_msg)
|
|
modified = False
|
|
cmd = ""
|
|
while True:
|
|
cmd = input('>')
|
|
if cmd=='p':
|
|
printraw(mem)
|
|
elif cmd=='f':
|
|
keys = input("keys:")
|
|
printfmt(mem, keys, shortcuts)
|
|
elif cmd=='a':
|
|
modified = True
|
|
obj = dict()
|
|
inputkv(obj, shortcuts)
|
|
mem.append(obj)
|
|
elif cmd=='m':
|
|
modified = True
|
|
pos = int(input("index:"))
|
|
inputkv(mem[pos], shortcuts)
|
|
elif cmd=='d':
|
|
modified = True
|
|
pos = int(input("index:"))
|
|
print("deleting %d: "%pos + '\t'.join([k+":"+v for k,v in mem[pos].items()]))
|
|
del mem[pos]
|
|
elif cmd=='q':
|
|
if modified:
|
|
print("Memory has been modified, to save, use 'wq', to exit without save, use 'q!'")
|
|
else:
|
|
break
|
|
elif cmd=='wq':
|
|
savedb(db_name, encode_method, db_obj, password)
|
|
break
|
|
elif cmd=='q!':
|
|
break
|
|
elif cmd=="sc":
|
|
print(shortcuts)
|
|
db_obj["shortcuts"] = inputshortcuts()
|
|
else:
|
|
print("Invalid Command, "+help_msg)
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
parser = argparse.ArgumentParser(description='Operate on a list of dicts in the cmdline and\
|
|
store them encrypted on the filesystem')
|
|
parser.add_argument('db_name')
|
|
parser.add_argument('decode_method')
|
|
parser.add_argument('encode_method', nargs="?")
|
|
parser.add_argument('--create', action='store_true')
|
|
# db_pass should not be passed in as cmdline args as they are logged and appear in .bash_history
|
|
args = parser.parse_args()
|
|
if args.encode_method==None:
|
|
args.encode_method = args.decode_method
|
|
if args.create and (os.path.isfile(args.db_name+'.store') or os.path.isfile(args.db_name+'.json')):
|
|
print("Cannot overwrite existing db")
|
|
exit(-1)
|
|
password=None
|
|
if args.decode_method=="fernet" or args.encode_method=="fernet":
|
|
password = input("password:").encode('utf-8')
|
|
interact(args.db_name, args.decode_method, args.encode_method, password, args.create) |