indico: wip: add LDAP support

Signed-off-by: Jeltz <jeltz@federez.net>
This commit is contained in:
jeltz 2025-02-22 12:47:58 +01:00
parent 0a8ae58334
commit dd2afc2cfb
Signed by: jeltz
GPG key ID: 800882B66C0C3326
2 changed files with 143 additions and 29 deletions

View file

@ -14,7 +14,9 @@ let
pythonEnv = pkgs.python3.withPackages (ps: [
(ps.toPythonModule indico)
ps.gunicorn
]);
]
++ lib.optionals (cfg.ldap != null)
indico.optional-dependencies.ldap);
redisSocket = config.services.redis.servers.${cfg.redis.name}.unixSocket;
indicoSocket = "/run/indico/indico.sock";
baseDir = "${pythonEnv}/${pythonEnv.sitePackages}/indico";
@ -36,7 +38,23 @@ let
format = "%(levelname)s %(request_id)s %(user_id)s %(name)s %(message)s";
};
};
configFile = pythonFmt.generate "indico.conf" {
ldapConfig = {
uri = cfg.ldap.uri;
bind_dn = cfg.ldap.bindDN;
bind_password = cfg.ldap.bindPassword;
timeout = 30;
verify_cert = true;
page_size = 1500;
uid = cfg.ldap.uid;
user_base = cfg.ldap.userBaseDN;
user_filter = cfg.ldap.userFilter;
gid = cfg.ldap.gid;
group_base = cfg.ldap.groupBaseDN;
group_filter = cfg.ldap.groupFilter;
member_of_attr = cfg.ldap.memberOf;
ad_group_style = false;
};
configFile = pythonFmt.generate "indico.conf" ({
SQLALCHEMY_DATABASE_URI = cfg.database;
CACHE_DIR = "${cfg.stateDir}/cache";
TEMP_DIR = "${cfg.stateDir}/tmp";
@ -64,9 +82,27 @@ let
SMTP_LOGIN = cfg.email.smtp.login;
SMTP_PASSWORD = cfg.email.smtp.password;
SMTP_USE_TLS = cfg.email.smtp.useTLS;
} // lib.optionalAttrs (cfg.ldap != null) {
AUTH_PROVIDERS = {
ldap = {
type = "ldap";
title = "LDAP";
ldap = ldapConfig;
default = true;
};
in
{
};
IDENTITY_PROVIDERS = {
ldap = {
type = "ldap";
title = "LDAP";
ldap = ldapConfig;
mapping = cfg.ldap.mapping;
trusted_email = cfg.ldap.trustedEmail;
synced_fields = cfg.ldap.syncedFields;
};
};
});
in {
# TODO cProfile; indico standalone command is *very* slow
# (~30s just to print the help)
@ -235,6 +271,91 @@ in
"postgresql://@/${cfg.user}?host=/run/postgresql";
description = "Database URL.";
};
ldap = lib.mkOption {
type = lib.types.nullOr (lib.types.submodule {
options = {
uri = lib.mkOption {
type = lib.types.str;
description = "LDAP server URI.";
};
bindDN = lib.mkOption {
type = lib.types.str;
description = "LDAP server bind DN.";
};
bindPassword = lib.mkOption {
type = lib.types.str;
description = "LDAP server bind password.";
};
uid = lib.mkOption {
type = lib.types.str;
default = "uid";
description = "LDAP UID attribute.";
};
userBaseDN = lib.mkOption {
type = lib.types.str;
description = "LDAP users base DN.";
};
userFilter = lib.mkOption {
type = lib.types.str;
description = "LDAP users filter.";
};
gid = lib.mkOption {
type = lib.types.str;
default = "gid";
description = "LDAP GID attribute.";
};
groupBaseDN = lib.mkOption {
type = lib.types.str;
description = "LDAP groups base DN.";
};
groupFilter = lib.mkOption {
type = lib.types.str;
description = "LDAP groups filter.";
};
memberOf = lib.mkOption {
type = lib.types.str;
default = "memberOf";
description = "LDAP memberOf attribute.";
};
mapping = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {
first_name = "givenName";
last_name = "sn";
email = "mail";
affiliation = "company";
phone = "telephoneNumber";
};
description = "Mapping between local and LDAP fields.";
};
trustedEmail = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether LDAP emails are to be trusted.";
};
syncedFields = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "first_name" "last_name" ];
description = "Fields to sync with LDAP server.";
};
};
});
default = null;
description = "LDAP authentication and identity provider configuration.";
};
};
config = lib.mkIf cfg.enable {
@ -254,12 +375,10 @@ in
package = pkgs.postgresql_16; # TODO add setting
# TODO db name != cfg.user
ensureDatabases = [ cfg.user ];
ensureUsers = [
{
ensureUsers = [{
name = cfg.user;
ensureDBOwnership = true;
}
];
}];
};
environment.systemPackages =
@ -268,7 +387,7 @@ in
#!${pkgs.runtimeShell}
sudo=exec
if [[ "$USER" != "indico" ]]; then
sudo='exec /run/wrappers/bin/sudo -u indico -E INDICO_CONFIG'
sudo='exec /run/wrappers/bin/sudo -u indico --preserve-env=INDICO_CONFIG'
fi
export INDICO_CONFIG=${configFile}
$sudo ${lib.getExe' pythonEnv "indico"} "$@"
@ -311,9 +430,7 @@ in
{
indico-db = lib.recursiveUpdate common {
description = "Indico database preparation and upgrade";
after = [
"postgresql.service"
];
after = [ "postgresql.service" ];
serviceConfig.Type = "oneshot";
# Source: pretalx module ; passer par un service oneshot
script = ''

View file

@ -69,7 +69,6 @@ python.pkgs.buildPythonApplication rec {
INDICO_NO_GIT = "true";
INDICO_MAP_VERSION = src.outputHash;
# TODO bin/maintenance/build-{wheel,assets}.py
build-system = [
python.pkgs.hatchling
python.pkgs.hatch-requirements-txt
@ -97,7 +96,7 @@ python.pkgs.buildPythonApplication rec {
# FIXME *why* is it required?
makeCacheWritable = true;
npmFlags = [ "--verbose" ];
# npmFlags = [ "--verbose" ];
patches = [
./remove_marshmallow_enum.patch
@ -137,11 +136,9 @@ python.pkgs.buildPythonApplication rec {
# TODO TODO ./bin/maintenance/build-assets.py
'';
# TODO build-assets.py
# build_urls_map.py tries to import indico.web.flask.app
# which has not been installed yet at this stage
preBuild = ''
ls -lah
PYTHONPATH="$(pwd):$PYTHONPATH" ${lib.getExe python} bin/maintenance/build-assets.py indico --clean
'';
@ -233,9 +230,9 @@ python.pkgs.buildPythonApplication rec {
++ sentry-sdk_1.optional-dependencies.pure_eval
++ wtforms.optional-dependencies.email;
# passthru = {
# inherit python;
# };
optional-dependencies = {
ldap = [ python.pkgs.python-ldap ];
};
meta = {
description = "Full-featured conferency lifecycle management and meeting/lecture scheduling tool";