2025-04-19 22:35:35 -07:00

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)