add option to create forgejo users declaratively

This commit is contained in:
ibizaman 2025-01-26 22:27:22 +01:00 committed by Pierre Penninckx
parent b34f2e3cca
commit d5293825e9
4 changed files with 90 additions and 43 deletions

View file

@ -18,7 +18,7 @@ Template:
## New Features
- Add option to select name of admin user for Forgejo.
- Add `shb.forgejo.users` option to create users declaratively.
## Fixes

View file

@ -968,32 +968,38 @@
"services-forgejo-options": [
"services-forgejo.html#services-forgejo-options"
],
"services-forgejo-options-shb.forgejo.adminUsername": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminUsername"
"services-forgejo-options-shb.forgejo.users": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users"
],
"services-forgejo-options-shb.forgejo.adminPassword": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword"
"services-forgejo-options-shb.forgejo.users._name_.email": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.email"
],
"services-forgejo-options-shb.forgejo.adminPassword.request": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword.request"
"services-forgejo-options-shb.forgejo.users._name_.isAdmin": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.isAdmin"
],
"services-forgejo-options-shb.forgejo.adminPassword.request.group": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword.request.group"
"services-forgejo-options-shb.forgejo.users._name_.password": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password"
],
"services-forgejo-options-shb.forgejo.adminPassword.request.mode": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword.request.mode"
"services-forgejo-options-shb.forgejo.users._name_.password.request": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password.request"
],
"services-forgejo-options-shb.forgejo.adminPassword.request.owner": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword.request.owner"
"services-forgejo-options-shb.forgejo.users._name_.password.request.group": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password.request.group"
],
"services-forgejo-options-shb.forgejo.adminPassword.request.restartUnits": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword.request.restartUnits"
"services-forgejo-options-shb.forgejo.users._name_.password.request.mode": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password.request.mode"
],
"services-forgejo-options-shb.forgejo.adminPassword.result": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword.result"
"services-forgejo-options-shb.forgejo.users._name_.password.request.owner": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password.request.owner"
],
"services-forgejo-options-shb.forgejo.adminPassword.result.path": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.adminPassword.result.path"
"services-forgejo-options-shb.forgejo.users._name_.password.request.restartUnits": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password.request.restartUnits"
],
"services-forgejo-options-shb.forgejo.users._name_.password.result": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password.result"
],
"services-forgejo-options-shb.forgejo.users._name_.password.result.path": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.users._name_.password.result.path"
],
"services-forgejo-options-shb.forgejo.backup": [
"services-forgejo.html#services-forgejo-options-shb.forgejo.backup"

View file

@ -5,8 +5,8 @@ let
contracts = pkgs.callPackage ../contracts {};
inherit (lib) getExe lists literalExpression mkBefore mkEnableOption mkForce mkIf mkMerge mkOption mkOverride optionals;
inherit (lib.types) bool enum listOf nullOr package port submodule str;
inherit (lib) all attrNames concatMapStringsSep getExe lists literalExpression mapAttrsToList mkBefore mkEnableOption mkForce mkIf mkMerge mkOption mkOverride nameValuePair optionalString optionals;
inherit (lib.types) attrsOf bool enum listOf nullOr package port submodule str;
in
{
options.shb.forgejo = {
@ -170,22 +170,37 @@ in
};
};
adminUsername = mkOption {
type = str;
description = "Forgejo admin user name.";
default = "admin";
};
users = mkOption {
description = "Users managed declaratively.";
type = attrsOf (submodule {
options = {
isAdmin = mkOption {
description = "Set user as admin or not.";
type = bool;
default = false;
};
adminPassword = mkOption {
description = "Forgejo admin user password.";
type = submodule {
options = contracts.secret.mkRequester {
mode = "0440";
owner = "forgejo";
group = "forgejo";
restartUnits = [ "forgejo.service" ];
email = mkOption {
description = ''Email of user.
This is only set when the user is created, changing this later on will have no effect.
'';
type = str;
};
password = mkOption {
description = "Forgejo admin user password.";
type = submodule {
options = contracts.secret.mkRequester {
mode = "0440";
owner = "forgejo";
group = "forgejo";
restartUnits = [ "forgejo.service" ];
};
};
};
};
};
});
};
databasePassword = mkOption {
@ -503,16 +518,20 @@ in
(mkIf cfg.enable {
assertions = [
{
assertion = cfg.adminUsername != "admin";
message = "Admin username cannot be 'admin'.";
assertion = all (u: u != "admin") (attrNames cfg.users);
message = "Username cannot be 'admin'.";
}
];
systemd.services.forgejo.preStart = ''
admin="${getExe config.services.forgejo.package} admin user"
$admin create --admin --must-change-password=false --email "root@localhost" --username "${cfg.adminUsername}" --password "$(tr -d '\n' < ${cfg.adminPassword.result.path})" || true
$admin change-password --must-change-password=false --username "${cfg.adminUsername}" --password "$(tr -d '\n' < ${cfg.adminPassword.result.path})" || true
'';
'' + concatMapStringsSep "\n" (u: ''
if ! $admin list | grep "${u.name}"; then
$admin create ${optionalString u.value.isAdmin "--admin"} --email "${u.value.email}" --must-change-password=false --username "${u.name}" --password "$(tr -d '\n' < ${u.value.password.result.path})"
else
$admin change-password --must-change-password=false --username "${u.name}" --password "$(tr -d '\n' < ${u.value.password.result.path})"
fi
'') (mapAttrsToList nameValuePair cfg.users);
})
(mkIf (cfg.enable && cfg.smtp != null) {

View file

@ -34,8 +34,17 @@ let
enable = true;
inherit (config.test) subdomain domain;
adminUsername = "theadmin";
adminPassword.result = config.shb.hardcodedsecret.forgejoAdminPassword.result;
users = {
"theadmin" = {
isAdmin = true;
email = "theadmin@example.com";
password.result = config.shb.hardcodedsecret.forgejoAdminPassword.result;
};
"theuser" = {
email = "theuser@example.com";
password.result = config.shb.hardcodedsecret.forgejoUserPassword.result;
};
};
databasePassword.result = config.shb.hardcodedsecret.forgejoDatabasePassword.result;
};
@ -45,10 +54,15 @@ let
};
shb.hardcodedsecret.forgejoAdminPassword = {
request = config.shb.forgejo.adminPassword.request;
request = config.shb.forgejo.users."theadmin".password.request;
settings.content = adminPassword;
};
shb.hardcodedsecret.forgejoUserPassword = {
request = config.shb.forgejo.users."theuser".password.request;
settings.content = "userPassword";
};
shb.hardcodedsecret.forgejoDatabasePassword = {
request = config.shb.forgejo.databasePassword.request;
settings.content = "databasePassword";
@ -78,6 +92,14 @@ let
"expect(page.get_by_role('button', name=re.compile('Sign In'))).not_to_be_visible()"
"expect(page).to_have_title(re.compile('Dashboard'))"
]; }
{ username = "theuser"; password = "userPasswordOops"; nextPageExpect = [
"expect(page.get_by_text('Username or password is incorrect.')).to_be_visible()"
]; }
{ username = "theuser"; password = "userPassword"; nextPageExpect = [
"expect(page.get_by_text('Username or password is incorrect.')).not_to_be_visible()"
"expect(page.get_by_role('button', name=re.compile('Sign In'))).not_to_be_visible()"
"expect(page).to_have_title(re.compile('Dashboard'))"
]; }
];
};
};