bean spiller

This commit is contained in:
Q
2023-01-22 13:31:46 +02:00
parent 115f631743
commit cd37185d2a
6 changed files with 251 additions and 8 deletions

View File

@@ -22,10 +22,7 @@ pipx: ## Install all packages with pipx
for module in *; do if [ -f $$module/setup.py ]; then pipx install $$module; fi; done
format: ## Reformat packages with black
black SimpleWebPage/
black TSVFilter/
black markslider/
black ffmpeg-parser/
for module in *; do if [ -f $$module/setup.py ]; then black $$module/; fi; done
tar-SimpleWebPage: clean ## Create package for SimpleWebPage
tar czf SimpleWebPage.tgz SimpleWebPage/

View File

@@ -1056,8 +1056,7 @@ def get_footer(readme, show_wget=True):
return """</tbody></table>{README}
</div>{WGET_STR}
</body></html>""".format(
README=readme,
WGET_STR=wget_str
README=readme, WGET_STR=wget_str
)

View File

@@ -156,7 +156,7 @@ Sample Rate: {sample_rate} Hz
def _mbstr(self, b):
try:
return "{:.1f}".format((float(b) / (1024 ** 2)))
return "{:.1f}".format((float(b) / (1024**2)))
except Exception:
return b
@@ -164,7 +164,7 @@ Sample Rate: {sample_rate} Hz
return self._sizefmt(float(b), suffix="bit/s")
try:
return "{:.1f}".format((float(b) / (1024 ** 2)))
return "{:.1f}".format((float(b) / (1024**2)))
except Exception:
return b

View File

@@ -0,0 +1,25 @@
from distutils.core import setup
import os
def version_reader(path):
for line in open(path, "rt").read(1024).split("\n"):
if line.startswith("__version__"):
return line.split("=")[1].strip().replace('"', "")
version = version_reader(os.path.join("spiller", "__init__.py"))
setup(
name="spiller",
packages=["spiller"],
version=version,
description="Very simple password storage, that encrypts with GPG cmdline tool.",
author="Ville Rantanen",
author_email="ville.q.rantanen@gmail.com",
entry_points={
"console_scripts": [
"spill = spiller.spiller:main",
]
},
)

View File

@@ -0,0 +1,2 @@
__version__ = "0.1"
from spiller.spiller import retrieve, store, list_storage

View File

@@ -0,0 +1,220 @@
#!/usr/bin/env python3
import json
import sys
import argparse
import os
import subprocess
import random
import stat
import string
JSON = os.getenv(
"SPILLER_STORAGE", os.path.expanduser("~/.config/spiller/storage.json")
)
def get_opts():
parser = argparse.ArgumentParser(
prog="spill",
description="""Key/value storage that uses JSON and GPG as backend.
Values are encrypted symmetrically with the key provided, or a random string is generated. Storage file can be changed with
SPILLER_STORAGE env variable. Encryption key can be passed from a variable SPILLER_KEY instead of a switch.""",
)
subparsers = parser.add_subparsers(dest="command", help="Command")
set_parser = subparsers.add_parser("set")
get_parser = subparsers.add_parser("get")
del_parser = subparsers.add_parser("list")
del_parser = subparsers.add_parser("del")
set_parser.add_argument(
"name",
action="store",
help="Name of secret",
)
set_parser.add_argument(
"data",
action="store",
help="Data to store",
)
set_parser.add_argument(
"--plain", action="store_true", default=False, help="Do not encrypt"
)
set_parser.add_argument(
"--key",
action="store",
default=os.getenv("SPILLER_KEY", None),
help="Encryption key",
)
get_parser.add_argument(
"name",
action="store",
help="Name of secret",
)
get_parser.add_argument(
"--key",
action="store",
default=os.getenv("SPILLER_KEY", None),
help="Decryption key",
)
del_parser.add_argument(
"name",
action="store",
help="Name of secret to delete",
)
return parser.parse_args()
def list_storage():
"""
Get list of keys in the secret storage
Args:
Returns:
List[List]: List of name and encryption method
"""
storage = load_storage()
names = []
for name in sorted(storage.keys()):
names.append([name, storage[name]["encryption"]])
names.sort(key=lambda x: x[1])
return names
def load_storage():
try:
with open(JSON, "rt") as fp:
return json.load(fp)
except FileNotFoundError:
return {}
def save_storage(storage):
if not os.path.exists(JSON):
os.makedirs(os.path.dirname(JSON), exist_ok=True)
with open(JSON, "wt") as fp:
json.dump(storage, fp, indent=2)
try:
os.chmod(JSON, stat.S_IWUSR | stat.S_IREAD)
except Exception:
pass
def del_storage(name):
storage = load_storage()
del storage[name]
save_storage(storage)
print("Deleted " + name)
def store(name, data, key, plain):
"""
Store key to secrets storage.
Args:
name (str): Name of the secret.
data (str): Data to encrypt.
key (str): Encryption key. If None, randomly generate the key
plain (bool): If set, stores the data as is, without encryption
Returns:
str: Key used to encrypt, useful if it was generated.
"""
entry = {"encryption": "gpg", "data": data}
storage = load_storage()
if plain:
entry["encryption"] = "none"
else:
if key == None:
key = get_random_key()
print("Random key: " + key)
entry["data"] = encrypt(data, key)
storage[name] = entry
save_storage(storage)
return key
def retrieve(name, key=None):
"""
Retrieve a secret from storage
Args:
name (str): Name of the secret.
key (str): Encryption key, if any required.
Returns:
str: Decrypted secret
"""
storage = load_storage()
entry = storage[name]
if entry["encryption"] == "none":
return entry["data"]
return decrypt(entry["data"], key)
def encrypt(data, key):
p = subprocess.run(
["gpg", "-a", "--symmetric", "--batch", "--passphrase-fd", "0"],
input=f"{key}\n{data}".encode(),
capture_output=True,
)
encrypted = p.stdout.decode()
if encrypted == "":
print("Encrypt failed!", file=sys.stderr)
sys.exit(1)
return encrypted
def decrypt(encrypted, key):
if key == None:
print("Requires --key!", file=sys.stderr)
sys.exit(1)
p = subprocess.run(
["gpg", "-d", "--batch", "--passphrase-fd", "0"],
input=f"{key}\n{encrypted}".encode(),
capture_output=True,
)
data = p.stdout.decode()
if data == "":
print("Decrypt failed!", file=sys.stderr)
sys.exit(1)
return data
def get_random_key():
return "-".join(
[
"".join(
[random.choice(string.ascii_letters + string.digits) for x in range(8)]
)
for x in range(5)
]
)
def main():
opts = get_opts()
if opts.command == "set":
store(opts.name, opts.data, opts.key, opts.plain)
if opts.command == "get":
print(retrieve(opts.name, opts.key))
if opts.command == "list":
names = list_storage()
names.insert(0, ["Name", "Encryption"])
padlen = max([len(x[0]) for x in names])
for row in names:
print(("{:" + str(padlen) + "} {}").format(*row))
if opts.command == "del":
del_storage(opts.name)