Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/uu/useradd/src/useradd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,9 @@ fn parse_options(matches: &clap::ArgMatches) -> Result<UseraddOptions, UseraddEr
let explicit_no_user_group = matches.get_flag(options::NO_USER_GROUP);
let create_user_group = if explicit_no_user_group {
false
} else if explicit_user_group || gid.is_none() {
} else if explicit_user_group {
true
} else if gid.is_none() {
// Default behavior: create user group when no -g specified.
// USERGROUPS_ENAB in login.defs controls this default.
let usergroups_enab = defs.get("USERGROUPS_ENAB").unwrap_or("yes");
Expand Down Expand Up @@ -1191,6 +1193,7 @@ pub fn uu_app() -> Command {
.short('U')
.long("user-group")
.action(ArgAction::SetTrue)
.conflicts_with(options::GID)
.help("Also create a matching user-private group (default)"),
)
.arg(
Expand Down Expand Up @@ -1403,6 +1406,15 @@ mod tests {
assert!(result.is_err());
}

#[test]
fn test_clap_user_group_conflicts_with_gid() {
let result = uu_app().try_get_matches_from(["useradd", "-U", "-g", "100", "user"]);
assert_eq!(
result.unwrap_err().kind(),
clap::error::ErrorKind::ArgumentConflict
);
}

#[test]
fn test_clap_non_unique_requires_uid() {
let result = uu_app().try_get_matches_from(["useradd", "-o", "user"]);
Expand Down
22 changes: 22 additions & 0 deletions tests/by-util/test_useradd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,28 @@ fn test_create_user_with_key_long_option() {
);
}

#[test]
fn test_user_group_flag_wins_over_usergroups_enab_no() {
if crate::common::skip_unless_root() {
return;
}

let dir = setup_root_dir();
let defs = dir.path().join("etc/login.defs");
let content = std::fs::read_to_string(&defs).expect("read login.defs");
std::fs::write(
&defs,
content.replace("USERGROUPS_ENAB yes", "USERGROUPS_ENAB no"),
)
.expect("write login.defs");

// USERGROUPS_ENAB only sets the default; -U asks for the group explicitly
assert_eq!(run_with_root(&dir, &["-U", "ugu"]), 0);
let group = read_group(&dir);
assert!(group.contains("ugu:x:1000:"), "got: {group}");
assert!(read_passwd(&dir).contains("ugu:x:1000:1000:"));
}

#[test]
fn test_create_system_user_with_key_sys_uid_range() {
if crate::common::skip_unless_root() {
Expand Down
Loading