nixos/user-groups: add a toggle for user account creation (#358646)

This commit is contained in:
Sandro
2025-02-27 23:47:20 +01:00
committed by GitHub
6 changed files with 123 additions and 12 deletions
@@ -555,6 +555,8 @@
- `gerbera` now has wavpack support.
- A toggle has been added under `users.users.<name>.enable` to allow toggling individual users conditionally. If set to false, the user account will not be created.
- `ddclient` was updated from 3.11.2 to 4.0.0 [Release notes](https://github.com/ddclient/ddclient/releases/tag/v4.0.0)
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+11 -1
View File
@@ -124,6 +124,16 @@ let
options = {
enable = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
If set to false, the user account will not be created. This is useful for when you wish to conditionally
disable user accounts.
'';
};
name = mkOption {
type = types.passwdEntry types.str;
apply = x: assert (stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
@@ -557,7 +567,7 @@ let
autoSubUidGidRange subUidRanges subGidRanges
initialPassword initialHashedPassword expires;
shell = utils.toShellPath u.shell;
}) cfg.users;
}) (filterAttrs (_: u: u.enable) cfg.users);
groups = attrValues cfg.groups;
});
+18 -11
View File
@@ -31,7 +31,7 @@ let
;
isNormal = opts.isNormalUser;
shell = utils.toShellPath opts.shell;
}) config.users.users;
}) (lib.filterAttrs (_: u: u.enable) config.users.users);
};
userbornConfigJson = pkgs.writeText "userborn.json" (builtins.toJSON userbornConfig);
@@ -95,16 +95,23 @@ in
# Create home directories, do not create /var/empty even if that's a user's
# home.
tmpfiles.settings.home-directories = lib.mapAttrs' (
username: opts:
lib.nameValuePair (toString opts.home) {
d = {
mode = opts.homeMode;
user = opts.name;
inherit (opts) group;
};
}
) (lib.filterAttrs (_username: opts: opts.createHome && opts.home != "/var/empty") userCfg.users);
tmpfiles.settings.home-directories =
lib.mapAttrs'
(
username: opts:
lib.nameValuePair (toString opts.home) {
d = {
mode = opts.homeMode;
user = opts.name;
inherit (opts) group;
};
}
)
(
lib.filterAttrs (
_username: opts: opts.enable && opts.createHome && opts.home != "/var/empty"
) userCfg.users
);
services.userborn = {
wantedBy = [ "sysinit.target" ];
+1
View File
@@ -1201,6 +1201,7 @@ in {
userborn-mutable-etc = runTest ./userborn-mutable-etc.nix;
userborn-immutable-etc = runTest ./userborn-immutable-etc.nix;
user-activation-scripts = handleTest ./user-activation-scripts.nix {};
user-enable-option = runTest ./user-enable-option.nix;
user-expiry = runTest ./user-expiry.nix;
user-home-mode = handleTest ./user-home-mode.nix {};
ustreamer = handleTest ./ustreamer.nix {};
+82
View File
@@ -0,0 +1,82 @@
let
normal-enabled = "username-normal-enabled";
normal-disabled = "username-normal-disabled";
system-enabled = "username-system-enabled";
system-disabled = "username-system-disabled";
passwd = "enableOptionPasswd";
in
{
name = "user-enable-option";
nodes.machine = {
users = {
groups.test-group = { };
users = {
# User is enabled (default behaviour).
${normal-enabled} = {
enable = true;
isNormalUser = true;
initialPassword = passwd;
};
# User is disabled.
${normal-disabled} = {
enable = false;
isNormalUser = true;
initialPassword = passwd;
};
# User is a system user, and is enabled.
${system-enabled} = {
enable = true;
isSystemUser = true;
initialPassword = passwd;
group = "test-group";
};
# User is a system user, and is disabled.
${system-disabled} = {
enable = false;
isSystemUser = true;
initialPassword = passwd;
group = "test-group";
};
};
};
};
testScript = ''
def switch_to_tty(tty_number):
machine.fail(f"pgrep -f 'agetty.*tty{tty_number}'")
machine.send_key(f"alt-f{tty_number}")
machine.wait_until_succeeds(f"[ $(fgconsole) = {tty_number} ]")
machine.wait_for_unit(f"getty@tty{tty_number}.service")
machine.wait_until_succeeds(f"pgrep -f 'agetty.*tty{tty_number}'")
machine.wait_for_unit("multi-user.target")
machine.wait_for_unit("getty@tty1.service")
with subtest("${normal-enabled} exists"):
check_fn = f"id ${normal-enabled}"
machine.succeed(check_fn)
machine.wait_until_tty_matches("1", "login: ")
machine.send_chars("${normal-enabled}\n")
machine.wait_until_tty_matches("1", "Password: ")
machine.send_chars("${passwd}\n")
with subtest("${normal-disabled} does not exist"):
switch_to_tty(2)
check_fn = f"id ${normal-disabled}"
machine.fail(check_fn)
with subtest("${system-enabled} exists"):
switch_to_tty(3)
check_fn = f"id ${system-enabled}"
machine.succeed(check_fn)
with subtest("${system-disabled} does not exist"):
switch_to_tty(4)
check_fn = f"id ${system-disabled}"
machine.fail(check_fn)
'';
}
+9
View File
@@ -66,6 +66,10 @@ in
isNormalUser = true;
hashedPassword = newNormaloHashedPassword;
};
normalo-disabled = {
enable = false;
isNormalUser = true;
};
};
groups = {
new-group = { };
@@ -96,6 +100,11 @@ in
assert 1000 > int(machine.succeed("id --user sysuser")), "sysuser user doesn't have a system UID"
assert "${sysuserInitialHashedPassword}" in machine.succeed("getent shadow sysuser"), "system user password is not correct"
with subtest("normalo-disabled is NOT created"):
machine.fail("id normalo-disabled")
# Check if user's home has been created
machine.fail("[ -d '/home/normalo-disabled' ]")
with subtest("sysusers group is created"):
print(machine.succeed("getent group sysusers"))