Allow undefined uids and gids when mutableUsers = true

Groups and users without gid/uid are created with
useradd/groupadd after the passwd/group merge phase
if mutableUsers = true.

This should fix #2114.
This commit is contained in:
Rickard Nilsson 2014-04-06 12:39:51 +02:00
parent 71ab7472d3
commit bf129a2c23

View File

@ -7,6 +7,9 @@ let
ids = config.ids; ids = config.ids;
cfg = config.users; cfg = config.users;
nonUidUsers = filterAttrs (n: u: u.uid == null) cfg.extraUsers;
nonGidGroups = filterAttrs (n: g: g.gid == null) cfg.extraGroups;
passwordDescription = '' passwordDescription = ''
The options <literal>hashedPassword</literal>, The options <literal>hashedPassword</literal>,
<literal>password</literal> and <literal>passwordFile</literal> <literal>password</literal> and <literal>passwordFile</literal>
@ -31,7 +34,10 @@ let
name = mkOption { name = mkOption {
type = types.str; type = types.str;
description = "The name of the user account. If undefined, the name of the attribute set will be used."; description = ''
The name of the user account. If undefined, the name of the
attribute set will be used.
'';
}; };
description = mkOption { description = mkOption {
@ -46,8 +52,14 @@ let
}; };
uid = mkOption { uid = mkOption {
type = with types; uniq int; type = with types; nullOr int;
description = "The account UID."; default = null;
description = ''
The account UID. If the <literal>mutableUsers</literal> option
is false, the UID cannot be null. Otherwise, the UID might be
null, in which case a free UID is picked on activation (by the
useradd command).
'';
}; };
group = mkOption { group = mkOption {
@ -151,12 +163,21 @@ let
name = mkOption { name = mkOption {
type = types.str; type = types.str;
description = "The name of the group. If undefined, the name of the attribute set will be used."; description = ''
The name of the group. If undefined, the name of the attribute set
will be used.
'';
}; };
gid = mkOption { gid = mkOption {
type = with types; uniq int; type = with types; nullOr int;
description = "The GID of the group."; default = null;
description = ''
The group GID. If the <literal>mutableUsers</literal> option
is false, the GID cannot be null. Otherwise, the GID might be
null, in which case a free GID is picked on activation (by the
groupadd command).
'';
}; };
members = mkOption { members = mkOption {
@ -218,13 +239,15 @@ let
groupFile = pkgs.writeText "group" ( groupFile = pkgs.writeText "group" (
concatStringsSep "\n" (map (g: mkGroupEntry g.name) ( concatStringsSep "\n" (map (g: mkGroupEntry g.name) (
sortOn "gid" (attrValues cfg.extraGroups) let f = g: g.gid != null; in
sortOn "gid" (filter f (attrValues cfg.extraGroups))
)) ))
); );
passwdFile = pkgs.writeText "passwd" ( passwdFile = pkgs.writeText "passwd" (
concatStringsSep "\n" (map (u: mkPasswdEntry u.name) ( concatStringsSep "\n" (map (u: mkPasswdEntry u.name) (
sortOn "uid" (filter (u: u.createUser) (attrValues cfg.extraUsers)) let f = u: u.createUser && (u.uid != null); in
sortOn "uid" (filter f (attrValues cfg.extraUsers))
)) ))
); );
@ -261,11 +284,11 @@ let
then builtins.trace "Duplicate ${idAttr} ${id}" { dup = true; acc = null; } then builtins.trace "Duplicate ${idAttr} ${id}" { dup = true; acc = null; }
else { dup = false; acc = newAcc; } else { dup = false; acc = newAcc; }
) { dup = false; acc = {}; } (builtins.attrNames set)).dup; ) { dup = false; acc = {}; } (builtins.attrNames set)).dup;
uidsAreUnique = idsAreUnique cfg.extraUsers "uid";
gidsAreUnique = idsAreUnique cfg.extraGroups "gid";
in
{ uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.extraUsers) "uid";
gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.extraGroups) "gid";
in {
###### interface ###### interface
@ -424,16 +447,31 @@ in
} }
fi fi
''; '';
mkhome = n: u: mkhome = n: u: ''
let uid="$(id -u ${u.name})"
uid = toString u.uid; gid="$(id -g ${u.name})"
gid = toString ((getGroup u.group).gid); h="${u.home}"
h = u.home; test -a "$h" || mkdir -p "$h" || true
in '' test "$(stat -c %u "$h")" = $uid || chown $uid "$h" || true
test -a "${h}" || mkdir -p "${h}" || true test "$(stat -c %g "$h")" = $gid || chgrp $gid "$h" || true
test "$(stat -c %u "${h}")" = ${uid} || chown ${uid} "${h}" || true '';
test "$(stat -c %g "${h}")" = ${gid} || chgrp ${gid} "${h}" || true groupadd = n: g: ''
''; if [ -z "$(getent group "${g.name}")" ]; then
echo "Adding group ${g.name}"
${pkgs.shadow}/sbin/groupadd "${g.name}"
fi
'';
useradd = n: u: ''
if ! id "${u.name}" &>/dev/null; then
echo "Adding user ${u.name}"
${pkgs.shadow}/sbin/useradd \
-g "${u.group}" \
-s "${u.shell}" \
-d "${u.home}" \
"${u.name}"
echo "${u.name}:x" | ${pkgs.shadow}/sbin/chpasswd -e
fi
'';
in stringAfter [ "etc" ] '' in stringAfter [ "etc" ] ''
touch /etc/group touch /etc/group
touch /etc/passwd touch /etc/passwd
@ -441,6 +479,8 @@ in
VISUAL=${merger passwdFile} ${pkgs.shadow}/sbin/vipw &>/dev/null VISUAL=${merger passwdFile} ${pkgs.shadow}/sbin/vipw &>/dev/null
${pkgs.shadow}/sbin/grpconv ${pkgs.shadow}/sbin/grpconv
${pkgs.shadow}/sbin/pwconv ${pkgs.shadow}/sbin/pwconv
${concatStrings (mapAttrsToList groupadd nonGidGroups)}
${concatStrings (mapAttrsToList useradd nonUidUsers)}
${concatStrings (mapAttrsToList mkhome mkhomeUsers)} ${concatStrings (mapAttrsToList mkhome mkhomeUsers)}
${concatStrings (mapAttrsToList setpw setpwUsers)} ${concatStrings (mapAttrsToList setpw setpwUsers)}
''; '';
@ -448,7 +488,17 @@ in
# for backwards compatibility # for backwards compatibility
system.activationScripts.groups = stringAfter [ "users" ] ""; system.activationScripts.groups = stringAfter [ "users" ] "";
assertions = [ { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique); message = "uids and gids must be unique!"; } ]; assertions = [
{ assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
message = "uids and gids must be unique!";
}
{ assertion = cfg.mutableUsers || (nonUidUsers == {});
message = "When mutableUsers is false, no uid can be null";
}
{ assertion = cfg.mutableUsers || (nonGidGroups == {});
message = "When mutableUsers is false, no gid can be null";
}
];
}; };