* Turn users.extraGroups into an attribute set (using types.loaOf).

Also the gid is looked up in ids.gids if not specified.

svn path=/nixos/trunk/; revision=33860
This commit is contained in:
Eelco Dolstra 2012-04-20 12:55:09 +00:00
parent 235ea24ec4
commit d587329615
2 changed files with 73 additions and 72 deletions

View File

@ -7,64 +7,75 @@ let
ids = config.ids; ids = config.ids;
users = config.users; users = config.users;
userOpts = {name, config, ...}: userOpts = { name, config, ... }: {
{
options = { options = {
name = mkOption { name = mkOption {
type = with types; uniq string; type = with types; uniq string;
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 {
type = with types; uniq string; type = with types; uniq string;
default = ""; default = "";
description = "A short description of the user account."; description = "A short description of the user account.";
}; };
uid = mkOption { uid = mkOption {
type = with types; uniq (nullOr int); type = with types; uniq (nullOr int);
default = null; default = null;
description = "The account UID. If undefined, NixOS will select a UID."; description = "The account UID. If undefined, NixOS will select a free UID.";
}; };
group = mkOption { group = mkOption {
type = with types; uniq string; type = with types; uniq string;
default = "nogroup"; default = "nogroup";
description = "The user's primary group."; description = "The user's primary group.";
}; };
extraGroups = mkOption { extraGroups = mkOption {
type = types.listOf types.string; type = types.listOf types.string;
default = []; default = [];
description = "The user's auxiliary groups."; description = "The user's auxiliary groups.";
}; };
home = mkOption { home = mkOption {
type = with types; uniq string; type = with types; uniq string;
default = "/var/empty"; default = "/var/empty";
description = "The user's home directory."; description = "The user's home directory.";
}; };
shell = mkOption { shell = mkOption {
type = with types; uniq string; type = with types; uniq string;
default = "/noshell"; default = "/noshell";
description = "The path to the user's shell."; description = "The path to the user's shell.";
}; };
createHome = mkOption { createHome = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = "If true, the home directory will be created automatically."; description = "If true, the home directory will be created automatically.";
}; };
useDefaultShell = mkOption { useDefaultShell = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>."; description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
}; };
password = mkOption { password = mkOption {
type = with types; uniq (nullOr string); type = with types; uniq (nullOr string);
default = null; default = null;
description = "The user's password. If undefined, no password is set for the user. Warning: do not set confidential information here because this data would be readable by all. This option should only be used for public account such as guest."; description = "The user's password. If undefined, no password is set for the user. Warning: do not set confidential information here because this data would be readable by all. This option should only be used for public account such as guest.";
}; };
isSystemUser = mkOption { isSystemUser = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
description = "Indicates if the user is a system user or not."; description = "Indicates if the user is a system user or not.";
}; };
createUser = mkOption { createUser = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
@ -74,6 +85,7 @@ let
then not modify any of the basic properties for the user account. then not modify any of the basic properties for the user account.
"; ";
}; };
}; };
config = { config = {
@ -81,71 +93,32 @@ let
uid = mkDefault (attrByPath [name] null ids.uids); uid = mkDefault (attrByPath [name] null ids.uids);
shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell); shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell);
}; };
}; };
# Groups to be created/updated by NixOS. groupOpts = { name, config, ... }: {
groups =
let options = {
defaultGroups =
[ { name = "root"; name = mkOption {
gid = ids.gids.root; type = with types; uniq string;
} description = "The name of the group. If undefined, the name of the attribute set will be used.";
{ name = "wheel"; };
gid = ids.gids.wheel;
} gid = mkOption {
{ name = "disk"; type = with types; uniq (nullOr int);
gid = ids.gids.disk; default = null;
} description = "The GID of the group. If undefined, NixOS will select a free GID.";
{ name = "kmem"; };
gid = ids.gids.kmem;
} };
{ name = "tty";
gid = ids.gids.tty;
}
{ name = "floppy";
gid = ids.gids.floppy;
}
{ name = "uucp";
gid = ids.gids.uucp;
}
{ name = "lp";
gid = ids.gids.lp;
}
{ name = "cdrom";
gid = ids.gids.cdrom;
}
{ name = "tape";
gid = ids.gids.tape;
}
{ name = "audio";
gid = ids.gids.audio;
}
{ name = "video";
gid = ids.gids.video;
}
{ name = "dialout";
gid = ids.gids.dialout;
}
{ name = "nogroup";
gid = ids.gids.nogroup;
}
{ name = "users";
gid = ids.gids.users;
}
{ name = "nixbld";
gid = ids.gids.nixbld;
}
{ name = "utmp";
gid = ids.gids.utmp;
}
];
addAttrs =
{ name, gid ? "" }:
{ inherit name gid; };
in map addAttrs (defaultGroups ++ config.users.extraGroups);
config = {
name = mkDefault name;
gid = mkDefault (attrByPath [name] null ids.gids);
};
};
# Note: the 'X' in front of the password is to distinguish between # Note: the 'X' in front of the password is to distinguish between
# having an empty password, and not having a password. # having an empty password, and not having a password.
@ -188,15 +161,16 @@ in
}; };
users.extraGroups = mkOption { users.extraGroups = mkOption {
default = []; default = {};
example = example =
[ { name = "students"; { students.gid = 1001;
gid = 1001; hackers = { };
} };
]; type = types.loaOf types.optionSet;
description = '' description = ''
Additional groups to be created automatically by the system. Additional groups to be created automatically by the system.
''; '';
options = [ groupOpts ];
}; };
}; };
@ -218,6 +192,26 @@ in
}; };
}; };
users.extraGroups = {
root = { };
wheel = { };
disk = { };
kmem = { };
tty = { };
floppy = { };
uucp = { };
lp = { };
cdrom = { };
tape = { };
audio = { };
video = { };
dialout = { };
nogroup = { };
users = { };
nixbld = { };
utmp = { };
};
system.activationScripts.rootPasswd = stringAfter [ "etc" ] system.activationScripts.rootPasswd = stringAfter [ "etc" ]
'' ''
# If there is no password file yet, create a root account with an # If there is no password file yet, create a root account with an
@ -313,7 +307,7 @@ in
fi fi
fi fi
done <<EndOfGroupList done <<EndOfGroupList
${concatStringsSep "\n" (map serializedGroup groups)} ${concatStringsSep "\n" (map serializedGroup (attrValues config.users.extraGroups))}
EndOfGroupList EndOfGroupList
''; '';

View File

@ -12,6 +12,13 @@
$machine->succeed("[ `nixos-version | wc -w` = 1 ]"); $machine->succeed("[ `nixos-version | wc -w` = 1 ]");
}; };
# Sanity check for uid/gid assignment.
subtest "users-groups", sub {
$machine->succeed("[ `id -u messagebus` = 4 ]");
$machine->succeed("[ `id -g messagebus` = 4 ]");
$machine->succeed("[ `getent group users` = 'users:x:100:' ]");
};
# Regression test for GMP aborts on QEMU. # Regression test for GMP aborts on QEMU.
subtest "gmp", sub { subtest "gmp", sub {
$machine->succeed("expr 1 + 2"); $machine->succeed("expr 1 + 2");