summaryrefslogtreecommitdiff
path: root/kvmd
diff options
context:
space:
mode:
Diffstat (limited to 'kvmd')
-rw-r--r--kvmd/application.py4
-rw-r--r--kvmd/apps/cleanup/__init__.py2
-rw-r--r--kvmd/apps/htpasswd/__init__.py91
-rw-r--r--kvmd/apps/htpasswd/__main__.py2
-rw-r--r--kvmd/apps/kvmd/__init__.py2
5 files changed, 97 insertions, 4 deletions
diff --git a/kvmd/application.py b/kvmd/application.py
index 8a3c40a0..4b8af949 100644
--- a/kvmd/application.py
+++ b/kvmd/application.py
@@ -23,8 +23,8 @@ from .yamlconf.loader import load_yaml_file
# =====
-def init() -> Tuple[argparse.ArgumentParser, List[str], Section]:
- args_parser = argparse.ArgumentParser(add_help=False)
+def init(prog: str=sys.argv[0], add_help: bool=True) -> Tuple[argparse.ArgumentParser, List[str], Section]:
+ args_parser = argparse.ArgumentParser(prog=prog, add_help=add_help)
args_parser.add_argument("-c", "--config", dest="config_path", default="/etc/kvmd/kvmd.yaml", metavar="<file>")
args_parser.add_argument("-o", "--set-options", dest="set_options", default=[], nargs="+")
args_parser.add_argument("-m", "--dump-config", dest="dump_config", action="store_true")
diff --git a/kvmd/apps/cleanup/__init__.py b/kvmd/apps/cleanup/__init__.py
index 53f46259..281f60cb 100644
--- a/kvmd/apps/cleanup/__init__.py
+++ b/kvmd/apps/cleanup/__init__.py
@@ -10,7 +10,7 @@ from ... import gpio
# =====
def main() -> None:
- config = init()[2].kvmd
+ config = init("kvmd-cleanup")[2].kvmd
logger = get_logger(0)
logger.info("Cleaning up ...")
diff --git a/kvmd/apps/htpasswd/__init__.py b/kvmd/apps/htpasswd/__init__.py
new file mode 100644
index 00000000..0eaf89f3
--- /dev/null
+++ b/kvmd/apps/htpasswd/__init__.py
@@ -0,0 +1,91 @@
+import sys
+import os
+import re
+import getpass
+import tempfile
+import contextlib
+import argparse
+
+from typing import Generator
+
+import passlib.apache
+
+from ...yamlconf import Section
+
+from ...application import init
+
+
+# =====
+def _get_htpasswd_for_write(config: Section) -> Generator[passlib.apache.HtpasswdFile, None, None]:
+ path = config.kvmd.auth.htpasswd
+ (tmp_fd, tmp_path) = tempfile.mkstemp(prefix=".", dir=os.path.dirname(path))
+ try:
+ try:
+ with open(path, "rb") as htpasswd_file:
+ os.write(tmp_fd, htpasswd_file.read())
+ finally:
+ os.close(tmp_fd)
+ htpasswd = passlib.apache.HtpasswdFile(tmp_path)
+ yield htpasswd
+ htpasswd.save()
+ os.rename(tmp_path, path)
+ finally:
+ if os.path.exists(tmp_path):
+ os.remove(tmp_path)
+
+
+def _valid_user(user: str) -> str:
+ stripped = user.strip()
+ if re.match(r"^[a-z_][a-z0-9_-]*$", stripped):
+ return stripped
+ raise SystemExit("Invalid user %r" % (user))
+
+
+# ====
+def _cmd_list(config: Section, _: argparse.Namespace) -> None:
+ for user in passlib.apache.HtpasswdFile(config.kvmd.auth.htpasswd).users():
+ print(user)
+
+
+def _cmd_set(config: Section, options: argparse.Namespace) -> None:
+ if options.read_stdin:
+ passwd = input()
+ else:
+ passwd = getpass.getpass("Password: ", stream=sys.stderr)
+ if getpass.getpass("Repeat: ", stream=sys.stderr) != passwd:
+ raise SystemExit("Sorry, passwords do not match")
+ with _get_htpasswd_for_write(config) as htpasswd:
+ htpasswd.set_password(options.user, passwd)
+
+
+def _cmd_delete(config: Section, options: argparse.Namespace) -> None:
+ with _get_htpasswd_for_write(config) as htpasswd:
+ htpasswd.delete(options.user)
+
+
+# =====
+def main() -> None:
+ (parent_parser, argv, config) = init(add_help=False)
+ parser = argparse.ArgumentParser(
+ prog="kvmd-htpasswd",
+ description="Manage KVMD users",
+ parents=[parent_parser],
+ )
+ parser.set_defaults(cmd=(lambda *_: parser.print_help()))
+ subparsers = parser.add_subparsers()
+
+ cmd_list_parser = subparsers.add_parser("list", help="List users")
+ cmd_list_parser.set_defaults(cmd=_cmd_list)
+
+ cmd_set_parser = subparsers.add_parser("set", help="Create user or change password")
+ cmd_set_parser.add_argument("user", type=_valid_user)
+ cmd_set_parser.add_argument("-i", "--read-stdin", action="store_true", help="Read password from stdin")
+ cmd_set_parser.set_defaults(cmd=_cmd_set)
+
+ cmd_delete_parser = subparsers.add_parser("del", help="Delete user")
+ cmd_delete_parser.add_argument("user", type=_valid_user)
+ cmd_delete_parser.set_defaults(cmd=_cmd_delete)
+
+ options = parser.parse_args(argv[1:])
+ options.cmd(config, options)
diff --git a/kvmd/apps/htpasswd/__main__.py b/kvmd/apps/htpasswd/__main__.py
new file mode 100644
index 00000000..031df43e
--- /dev/null
+++ b/kvmd/apps/htpasswd/__main__.py
@@ -0,0 +1,2 @@
+from . import main
+main()
diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py
index 57844cc5..08dfe0dd 100644
--- a/kvmd/apps/kvmd/__init__.py
+++ b/kvmd/apps/kvmd/__init__.py
@@ -17,7 +17,7 @@ from .server import Server
# =====
def main() -> None:
- config = init()[2].kvmd
+ config = init("kvmd")[2].kvmd
with gpio.bcm():
loop = asyncio.get_event_loop()