diff --git a/py-packages/spiller/pyproject.toml b/py-packages/spiller/pyproject.toml index 6db8eff..62d6456 100644 --- a/py-packages/spiller/pyproject.toml +++ b/py-packages/spiller/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ dependencies = [] [project.scripts] -spill="spiller.spiller:main" +spill="spiller:main" [tool.hatch.version] path = "spiller/__init__.py" diff --git a/py-packages/spiller/setup.py b/py-packages/spiller/setup.py index cc66d5c..9f2ba7b 100644 --- a/py-packages/spiller/setup.py +++ b/py-packages/spiller/setup.py @@ -15,11 +15,11 @@ setup( packages=["spiller"], version=version, description="Very simple password storage, that encrypts with GPG cmdline tool.", - author="Ville Rantanen", + author="Q", author_email="q@six9.net", entry_points={ "console_scripts": [ - "spill = spiller.spiller:main", + "spill = spiller:main", ] }, ) diff --git a/py-packages/spiller/spiller/__init__.py b/py-packages/spiller/spiller/__init__.py index 2eb004a..3d4c8c2 100644 --- a/py-packages/spiller/spiller/__init__.py +++ b/py-packages/spiller/spiller/__init__.py @@ -1,2 +1,117 @@ -__version__ = "0.4" -from spiller.spiller import Spiller, decrypt, encrypt +import argparse +import os + +from spiller.spiller import DEFAULT_CONFIG, DEFAULT_STORAGE, Spiller, decrypt, encrypt + +__version__ = "0.5" + + +def get_opts(): + parser = argparse.ArgumentParser( + prog="spill", + description=f"""Key/value storage that uses JSON and GPG as backend. + Values are encrypted symmetrically with the key provided, or a random string is generated. + Encryption key can be passed from a variable SPILLER_KEY instead of a switch. + Storage file can be changed with SPILLER_STORAGE env variable, in a + "SPILLER_STORAGE": key in a JSON file read at {DEFAULT_STORAGE} + """, + ) + parser.add_argument("--version", action="version", version=__version__) + parser.add_argument("-q", "--quiet", action="store_true", default=False) + + 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", + nargs="?", + help="Data to store. Must use this or --data-file.", + ) + set_parser.add_argument( + "--data-file", + action="store", + type=argparse.FileType("r"), + help="Read the data to store from a file. Must use this or [data]. Will strip newlines at the end.", + ) + 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 (or use SPILLER_KEY)", + ) + set_parser.add_argument( + "--key-file", + action="store", + default=None, + type=argparse.FileType("r"), + help="Read encryption key stored in a file", + ) + 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 (or use SPILLER_KEY)", + ) + get_parser.add_argument( + "--key-file", + action="store", + default=None, + type=argparse.FileType("r"), + help="Read encryption key stored in a file. Will strip newlines at the end.", + ) + del_parser.add_argument( + "name", + action="store", + help="Name of secret to delete", + ) + args = parser.parse_args() + if args.command is None: + raise parser.error("Command missing") + + try: + if args.key_file: + with args.key_file as fp: + args.key = fp.read().rstrip("\n") + except AttributeError: + pass + + if args.command == "set": + if args.data and args.data_file: + raise parser.error("Can not use both [data] and --data-file") + if args.data is None and args.data_file is None: + raise parser.error("Must use either [data] or --data-file") + if args.data_file: + with args.data_file as fp: + args.data = fp.read().rstrip("\n") + + return args + + +def main(): + opts = get_opts() + spill = Spiller() + spill.verbose = not opts.quiet + + if opts.command == "set": + spill.store(opts.name, opts.data, opts.key, opts.plain) + if opts.command == "get": + print(spill.retrieve(opts.name, opts.key), end="") + if opts.command == "list": + print(spill.format_storage()) + if opts.command == "del": + spill.del_storage(opts.name) diff --git a/py-packages/spiller/spiller/spiller.py b/py-packages/spiller/spiller/spiller.py index a37cad4..aef6a3b 100755 --- a/py-packages/spiller/spiller/spiller.py +++ b/py-packages/spiller/spiller/spiller.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import argparse import json import os import random @@ -9,98 +8,8 @@ import string import subprocess import sys - -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. - Encryption key can be passed from a variable SPILLER_KEY instead of a switch. - Storage file can be changed with SPILLER_STORAGE env variable, in a - "SPILLER_STORAGE": key in a JSON file read at ~/.config/spill/config.json - """, - ) - 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", - nargs="?", - help="Data to store. Must use this or --data-file.", - ) - set_parser.add_argument( - "--data-file", - action="store", - type=argparse.FileType("r"), - help="Read the data to store from a file. Must use this or [data]. Will strip newlines at the end.", - ) - 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 (or use SPILLER_KEY)", - ) - set_parser.add_argument( - "--key-file", - action="store", - default=None, - type=argparse.FileType("r"), - help="Read encryption key stored in a file", - ) - 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 (or use SPILLER_KEY)", - ) - get_parser.add_argument( - "--key-file", - action="store", - default=None, - type=argparse.FileType("r"), - help="Read encryption key stored in a file. Will strip newlines at the end.", - ) - del_parser.add_argument( - "name", - action="store", - help="Name of secret to delete", - ) - args = parser.parse_args() - if args.command is None: - raise parser.error("Command missing") - - try: - if args.key_file: - with args.key_file as fp: - args.key = fp.read().rstrip("\n") - except AttributeError: - pass - - if args.command == "set": - if args.data and args.data_file: - raise parser.error("Can not use both [data] and --data-file") - if args.data is None and args.data_file is None: - raise parser.error("Must use either [data] or --data-file") - if args.data_file: - with args.data_file as fp: - args.data = fp.read().rstrip("\n") - - return args +DEFAULT_CONFIG = os.path.expanduser("~/.config/spiller/config.json") +DEFAULT_STORAGE = os.path.expanduser("~/.config/spiller/storage.json") class Spiller: @@ -114,10 +23,10 @@ class Spiller: self.verbose = False def get_config(self): - default_config = {"SPILLER_STORAGE": os.path.expanduser("~/.config/spiller/storage.json")} + default_config = {"SPILLER_STORAGE": DEFAULT_STORAGE} try: - with open(os.path.expanduser("~/.config/spiller/config.json"), "rt") as fp: + with open(DEFAULT_CONFIG, "rt") as fp: default_config.update(json.load(fp)) except Exception: pass @@ -165,13 +74,13 @@ class Spiller: except Exception: pass - def del_storage(name): + def del_storage(self, name): """writes directly !""" del self.storage[name] self.save_storage() if self.verbose: - print("Deleted " + name) + print("Deleted " + name, file=sys.stderr) def store(self, name, data, key, plain): """ @@ -193,7 +102,7 @@ class Spiller: if key == None: key = get_random_key() if self.verbose: - print("Random key: " + key) + print("Random key: " + key, file=sys.stderr) entry["data"], ec = self.encrypt(data, key) if ec != 0: raise ValueError("Encryption Failed") @@ -234,7 +143,7 @@ class Spiller: if encrypted == "": if self.verbose: print("Encrypt failed!", file=sys.stderr) - None, 1 + return None, 1 return encrypted, 0 def decrypt(self, encrypted, key): @@ -271,18 +180,3 @@ 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() - spill = Spiller() - spill.verbose = True - - if opts.command == "set": - spill.store(opts.name, opts.data, opts.key, opts.plain) - if opts.command == "get": - print(spill.retrieve(opts.name, opts.key)) - if opts.command == "list": - print(spill.format_storage()) - if opts.command == "del": - spill.del_storage(opts.name)