#!/usr/bin/env python3 import sys as _sys import os as _os import subprocess as _subprocess import argparse as _argparse import yaml as _yaml import string as _string import random as _random def file_read(path): handle = open(path, "r") content = handle.read() handle.close() return content def file_write(path, content): directory = _os.path.dirname(path) if (not _os.path.exists(directory)): _os.makedirs(directory, exist_ok = True) else: pass handle = open(path, "w") handle.write(content) handle.close() return content def get_password_hash(binary_file_path, conf_file_path, name): output = _subprocess.check_output([ binary_file_path, "--config=%s" % conf_file_path, "hash-password", name ]) return output.decode("utf-8").split("\n")[0][8:] def postprocess(binary_file_path, conf_file_path, data): password = "".join(map(lambda x: _random.choice(_string.ascii_lowercase), range(16))) if (len(data["users"]) <= 0): data["users"]["_dummy"] = { "displayname": "(Dummy)", "password": get_password_hash(binary_file_path, conf_file_path, password), "email": "dummy@nowhere.net", "groups": [], } else: pass return data def data_read(user_file_path): data = _yaml.safe_load(file_read(user_file_path)) data["users"].pop("_dummy", None) return data def data_write(binary_file_path, conf_file_path, user_file_path, data): file_write(user_file_path, _yaml.dump(postprocess(binary_file_path, conf_file_path, data))) def main(): ## args argument_parser = _argparse.ArgumentParser() argument_parser.add_argument( "command", type = str, choices = ["list", "show", "add", "change", "remove"], default = "list", help = "command to execute", ) argument_parser.add_argument( "-b", "--binary-file-path", type = str, default = "/usr/bin/authelia", help = "path to the Authelia binary file", ) argument_parser.add_argument( "-c", "--conf-file-path", type = str, default = "/etc/authelia/configuration.yml", help = "path to the Authelia configuration file", ) argument_parser.add_argument( "-u", "--user-file-path", type = str, default = "/var/authelia/users.yml", help = "path to the user database file", ) argument_parser.add_argument( "-n", "--login-name", type = str, default = None, help = "login name of the user; has to be unique", ) argument_parser.add_argument( "-p", "--password", type = str, default = None, help = "password of the user", ) argument_parser.add_argument( "-d", "--display-name", type = str, default = None, help = "display name of the user", ) argument_parser.add_argument( "-e", "--email", type = str, default = None, help = "e-mail address of the user", ) argument_parser.add_argument( "-x", "--deactivated", type = str, default = "no", choices = ["no", "yes"], help = "whether the user shall be deactivated", ) args = argument_parser.parse_args() ## exec data = data_read(args.user_file_path) if (args.command == "list"): names = sorted(data["users"].keys()) for name in names: _sys.stdout.write("%s\n" % name) elif (args.command == "show"): if (args.login_name is None): raise ValueError("name required") else: if (not (args.login_name in data["users"])): raise ValueError("no such user") else: entry = data["users"][args.login_name] _sys.stdout.write("%s\n" % _yaml.dump(entry)) elif (args.command == "add"): if (args.login_name is None): raise ValueError("name required") else: if (args.password is None): raise ValueError("password required") else: entry = { "disabled": (args.deactivated == "yes"), "displayname": (args.display_name or args.login_name), "password": get_password_hash(args.binary_file_path, args.conf_file_path, args.login_name), "email": args.email, "groups": [], } data["users"][args.login_name] = entry data_write(args.binary_file_path, args.conf_file_path, args.user_file_path, data) elif (args.command == "change"): if (args.login_name is None): raise ValueError("name required") else: entry = data["users"][args.login_name] if (args.deactivated is None): pass else: entry["disabled"] = (args.deactivated == "yes") if (args.password is None): pass else: entry["password"] = get_password_hash(args.binary_file_path, args.conf_file_path, args.login_name) if (args.display_name is None): pass else: entry["displayname"] = args.display_name if (args.email is None): pass else: entry["email"] = args.email data["users"][args.login_name] = entry data_write(args.binary_file_path, args.conf_file_path, args.user_file_path, data) elif (args.command == "remove"): if (args.login_name is None): raise ValueError("name required") else: if (not (args.login_name in data["users"])): raise ValueError("no such user") else: del data["users"][args.login_name] data_write(args.binary_file_path, args.conf_file_path, args.user_file_path, data) else: raise NotImplementedError() main()