indico: use files for passwords

Signed-off-by: Jeltz <jeltz@federez.net>
This commit is contained in:
jeltz 2025-02-27 21:49:48 +01:00
parent dd2afc2cfb
commit 2f93570ac4
Signed by: jeltz
GPG key ID: 800882B66C0C3326
3 changed files with 120 additions and 13 deletions

View file

@ -8,8 +8,8 @@
let let
cfg = config.services.indico; cfg = config.services.indico;
pythonFmt = pkgs.formats.pythonVars { };
yamlFmt = pkgs.formats.yaml {}; yamlFmt = pkgs.formats.yaml {};
pythonFmt = pkgs.callPackage ../pkgs/python-vars-with-env { };
indico = pkgs.callPackage ../pkgs/indico { }; indico = pkgs.callPackage ../pkgs/indico { };
pythonEnv = pkgs.python3.withPackages (ps: [ pythonEnv = pkgs.python3.withPackages (ps: [
(ps.toPythonModule indico) (ps.toPythonModule indico)
@ -41,7 +41,7 @@ let
ldapConfig = { ldapConfig = {
uri = cfg.ldap.uri; uri = cfg.ldap.uri;
bind_dn = cfg.ldap.bindDN; bind_dn = cfg.ldap.bindDN;
bind_password = cfg.ldap.bindPassword; bind_password = pythonFmt.mkReadFile cfg.ldap.bindPasswordFile;
timeout = 30; timeout = 30;
verify_cert = true; verify_cert = true;
page_size = 1500; page_size = 1500;
@ -72,7 +72,7 @@ let
CELERY_BROKER = cfg.celeryBrokerRedis; CELERY_BROKER = cfg.celeryBrokerRedis;
USE_PROXY = true; USE_PROXY = true;
BASE_URL = cfg.baseUrl; BASE_URL = cfg.baseUrl;
SECRET_KEY = cfg.secretKey; SECRET_KEY = pythonFmt.mkReadFile cfg.secretKeyFile;
LOGGING_CONFIG_FILE = loggingFile; LOGGING_CONFIG_FILE = loggingFile;
NO_REPLY_EMAIL = cfg.email.noReply; NO_REPLY_EMAIL = cfg.email.noReply;
SUPPORT_EMAIL = cfg.email.support; SUPPORT_EMAIL = cfg.email.support;
@ -80,7 +80,7 @@ let
PUBLIC_SUPPORT_EMAIL = cfg.email.publicSupport; PUBLIC_SUPPORT_EMAIL = cfg.email.publicSupport;
SMTP_SERVER = [ cfg.email.smtp.host cfg.email.smtp.port ]; SMTP_SERVER = [ cfg.email.smtp.host cfg.email.smtp.port ];
SMTP_LOGIN = cfg.email.smtp.login; SMTP_LOGIN = cfg.email.smtp.login;
SMTP_PASSWORD = cfg.email.smtp.password; SMTP_PASSWORD = pythonFmt.mkReadFile cfg.email.smtp.passwordFile;
SMTP_USE_TLS = cfg.email.smtp.useTLS; SMTP_USE_TLS = cfg.email.smtp.useTLS;
} // lib.optionalAttrs (cfg.ldap != null) { } // lib.optionalAttrs (cfg.ldap != null) {
AUTH_PROVIDERS = { AUTH_PROVIDERS = {
@ -169,16 +169,16 @@ in {
description = "SMTP login."; description = "SMTP login.";
}; };
password = lib.mkOption { passwordFile = lib.mkOption {
type = lib.types.str; type = lib.types.path;
description = "SMTP password."; description = "SMTP password file.";
}; };
}; };
}; };
secretKey = lib.mkOption { secretKeyFile = lib.mkOption {
type = lib.types.str; type = lib.types.path;
description = "Secret key."; description = "Secret key file.";
}; };
baseUrl = lib.mkOption { baseUrl = lib.mkOption {
@ -285,9 +285,9 @@ in {
description = "LDAP server bind DN."; description = "LDAP server bind DN.";
}; };
bindPassword = lib.mkOption { bindPasswordFile = lib.mkOption {
type = lib.types.str; type = lib.types.path;
description = "LDAP server bind password."; description = "LDAP server bind password file.";
}; };
uid = lib.mkOption { uid = lib.mkOption {

View file

@ -0,0 +1,54 @@
from argparse import ArgumentParser
from pathlib import Path
from json import load
# Indico raises a warning for every non prefixed local variable, so we add a
# prefix to each import and declared function
PROLOGUE = (
"import functools as _functools\n"
"@_functools.cache\n"
"def _read_file(name):\n"
" with open(name) as f:\n"
" return name\n"
)
def make_value(value):
match value["_pyType"]:
case "str" | "int" | "bool" | "null":
return repr(value["_value"])
case "list":
items = [make_value(i) for i in value["_value"]]
return f"[{','.join(items)}]"
case "dict":
items = [
f"{repr(k)}: {make_value(v)}"
for k, v in value["_value"].items()
]
return f"{{{','.join(items)}}}"
case "read-file":
return f"_read_file({repr(value['_value'])})"
case _:
raise ValueError("Unknown data type")
def mk_vars(vars):
if not all(str.isidentifier(n) for n in vars.keys()):
raise ValueError("At least one variable identifier is invalid")
eqs = [f"{n} = {make_value(v)}" for n, v in vars.items()]
return PROLOGUE + "\n".join(eqs)
def main():
parser = ArgumentParser()
parser.add_argument("json", type=Path)
args = parser.parse_args()
with args.json.open() as fd:
print(mk_vars(load(fd)))
if __name__ == "__main__":
main()

View file

@ -0,0 +1,53 @@
{
lib,
pkgs,
...
}:
rec {
_mkPy = type: value: {
_pyType = type;
_value = value;
};
_isPy = value: lib.isAttrs value && lib.hasAttr "_pyType" value;
_mkValue = value:
if _isPy value then
value
else if lib.isStringLike value then
_mkPy "str" value
else if lib.isInt value then
_mkPy "int" value
else if lib.isBool value then
_mkPy "bool" value
else if value == null then
_mkPy "null" null
else if lib.isList value then
_mkPy "list" (lib.map _mkValue value)
else if lib.isAttrs value then
_mkPy "dict" (lib.mapAttrs (_: _mkValue) value)
else
abort "unsupported type";
_mkVars = lib.mapAttrs (_: _mkValue);
mkReadFile = path:
_mkPy "read-file" (if lib.isStringLike path then
path
else
abort "invalid path type");
generate = name: value:
pkgs.callPackage ({ runCommand, python3, black }:
runCommand name {
nativeBuildInputs = [ python3 black ];
value = builtins.toJSON (_mkVars value);
codegen = builtins.readFile ./codegen.py;
passAsFile = [ "codegen" "value" ];
preferLocalBuild = true;
} ''
python3 $codegenPath $valuePath > $out
black $out
'') { };
}