diff --git a/ansible/roles/authelia/files/conf-compose.py b/ansible/roles/authelia/files/conf-compose.py index 92a76b8..011babb 100644 --- a/ansible/roles/authelia/files/conf-compose.py +++ b/ansible/roles/authelia/files/conf-compose.py @@ -117,7 +117,25 @@ def main(): } } data = dict_merge(data, data_) - + ### postprocess + if True: + if (len(data["identity_providers"]["oidc"]["clients"]) <= 0): + data["identity_providers"]["oidc"]["clients"].append( + { + "public": False, + "id": "_dummy", + "description": "not a real client; just here to make Authelia run", + "authorization_policy": "one_factor", + "secret": "", + "scopes": [], + "redirect_uris": [], + "grant_types": [], + "response_types": [], + "response_modes": [], + } + ) + else: + pass ## output if True: if (args.output_format == "json"): diff --git a/ansible/roles/authelia/files/user-manage.py b/ansible/roles/authelia/files/user-manage.py new file mode 100644 index 0000000..7d9a84e --- /dev/null +++ b/ansible/roles/authelia/files/user-manage.py @@ -0,0 +1,188 @@ +#!/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", + ) + 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 = { + "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.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() diff --git a/ansible/roles/authelia/info.md b/ansible/roles/authelia/info.md index abdb2ac..6779bb2 100644 --- a/ansible/roles/authelia/info.md +++ b/ansible/roles/authelia/info.md @@ -1,7 +1,8 @@ ## Beschreibung - zum Aufsetzen des Authentifizierungs-Dienstes [Authelia](https://www.authelia.com/) -- um einen Clients hinzuzfügen, erstellt man eine entsprechende JSON-Datei in `/etc/authelia/conf.d/clients` und führt aus `/usr/bin/authelia-conf-compose` +- um einen Clients hinzuzfügen, erstellt man eine entsprechende JSON-Datei in `/etc/authelia/conf.d/clients` und führt aus `authelia-conf-compose` +- zum Verwalten der Nutzer kann man `authelia-user-manage` verwenden (`-h` anhängen für Erklärung) ## Verweise @@ -13,6 +14,4 @@ ## ToDo -- Dummy-Client los werden -- Dummy-Nutzer los werden -- Nutzer-Datei-Verwaltung verbessern +- Nutzer-Verwaltung verbessern (evtl. externalisieren) diff --git a/ansible/roles/authelia/tasks/main.json b/ansible/roles/authelia/tasks/main.json index 22cfd21..c765720 100644 --- a/ansible/roles/authelia/tasks/main.json +++ b/ansible/roles/authelia/tasks/main.json @@ -77,14 +77,6 @@ "dest": "/etc/authelia/conf.d/main.json" } }, - { - "name": "configuration | client | dummy", - "become": true, - "ansible.builtin.template": { - "src": "conf-client-dummy.json.j2", - "dest": "/etc/authelia/conf.d/clients/dummy.json" - } - }, { "name": "configuration | compose", "become": true, @@ -101,7 +93,7 @@ } }, { - "name": "setup users directory", + "name": "users | directory", "become": true, "ansible.builtin.file": { "state": "directory", @@ -109,13 +101,22 @@ } }, { - "name": "place dummy user file", + "name": "users | initial file", "become": true, "ansible.builtin.template": { "src": "users.yml.j2", "dest": "{{var_authelia_users_file_path}}" } }, + { + "name": "users | management script", + "become": true, + "ansible.builtin.copy": { + "src": "user-manage.py", + "dest": "/usr/bin/authelia-user-manage", + "mode": "0700" + } + }, { "name": "apply", "become": true, diff --git a/ansible/roles/authelia/templates/conf-client-dummy.json.j2 b/ansible/roles/authelia/templates/conf-client-dummy.json.j2 deleted file mode 100644 index 88dcf25..0000000 --- a/ansible/roles/authelia/templates/conf-client-dummy.json.j2 +++ /dev/null @@ -1,7 +0,0 @@ -{ - "public": false, - "id": "dummy", - "secret": "d1424b378e4fbbc153f330f33b74ab192525b98cc2dd58b2e8d01c2737be00c6", - "redirect_uris": [ - ] -} diff --git a/ansible/roles/authelia/templates/users.yml.j2 b/ansible/roles/authelia/templates/users.yml.j2 index eea8164..bf82e98 100644 --- a/ansible/roles/authelia/templates/users.yml.j2 +++ b/ansible/roles/authelia/templates/users.yml.j2 @@ -1,6 +1 @@ -users: - _dummy: - displayname: "(Dummy)" - password: "$argon2id$v=19$m=65536,t=3,p=4$sHIRjFaYRz2U3F8wHnqecQ$lwnQtHNeFqgLaLSW8It7KJSHNOJoSeF+RF7lwgM7WRA" - email: "dummy@nowhere.org" - groups: [] +users: {}