diff --git a/mock_ssh_test.go b/mock_ssh_test.go index 6e4b647..9f83885 100644 --- a/mock_ssh_test.go +++ b/mock_ssh_test.go @@ -1432,13 +1432,18 @@ func moduleUserWithClient(_ *Executor, client sshRunner, args map[string]any) (* name := getStringArg(args, "name", "") state := getStringArg(args, "state", "present") appendGroups := getBoolArg(args, "append", false) + local := getBoolArg(args, "local", false) if name == "" { return nil, mockError("moduleUserWithClient", "user: name required") } if state == "absent" { - cmd := sprintf("userdel -r %s 2>/dev/null || true", name) + delCmd := "userdel" + if local { + delCmd = "luserdel" + } + cmd := sprintf("%s -r %s 2>/dev/null || true", delCmd, name) _, _, _, _ = client.Run(context.Background(), cmd) return &TaskResult{Changed: true}, nil } @@ -1482,12 +1487,18 @@ func moduleUserWithClient(_ *Executor, client sshRunner, args map[string]any) (* // Try usermod first, then useradd addOptsStr := joinStrings(addOpts, " ") modOptsStr := joinStrings(modOpts, " ") + addCmd := "useradd" + modCmd := "usermod" + if local { + addCmd = "luseradd" + modCmd = "lusermod" + } var cmd string if addOptsStr == "" { - cmd = sprintf("id %s >/dev/null 2>&1 || useradd %s", name, name) + cmd = sprintf("id %s >/dev/null 2>&1 || %s %s", name, addCmd, name) } else { - cmd = sprintf("id %s >/dev/null 2>&1 && usermod %s %s || useradd %s %s", - name, modOptsStr, name, addOptsStr, name) + cmd = sprintf("id %s >/dev/null 2>&1 && %s %s %s || %s %s %s", + name, modCmd, modOptsStr, name, addCmd, addOptsStr, name) } stdout, stderr, rc, err := client.Run(context.Background(), cmd) diff --git a/modules.go b/modules.go index 8caa188..022fdea 100644 --- a/modules.go +++ b/modules.go @@ -1389,13 +1389,18 @@ func (e *Executor) moduleUser(ctx context.Context, client sshExecutorClient, arg name := getStringArg(args, "name", "") state := getStringArg(args, "state", "present") appendGroups := getBoolArg(args, "append", false) + local := getBoolArg(args, "local", false) if name == "" { return nil, coreerr.E("Executor.moduleUser", "name required", nil) } if state == "absent" { - cmd := sprintf("userdel -r %s 2>/dev/null || true", name) + delCmd := "userdel" + if local { + delCmd = "luserdel" + } + cmd := sprintf("%s -r %s 2>/dev/null || true", delCmd, name) _, _, _, _ = client.Run(ctx, cmd) return &TaskResult{Changed: true}, nil } @@ -1439,12 +1444,18 @@ func (e *Executor) moduleUser(ctx context.Context, client sshExecutorClient, arg // Try usermod first, then useradd addOptsStr := join(" ", addOpts) modOptsStr := join(" ", modOpts) + addCmd := "useradd" + modCmd := "usermod" + if local { + addCmd = "luseradd" + modCmd = "lusermod" + } var cmd string if addOptsStr == "" { - cmd = sprintf("id %s >/dev/null 2>&1 || useradd %s", name, name) + cmd = sprintf("id %s >/dev/null 2>&1 || %s %s", name, addCmd, name) } else { - cmd = sprintf("id %s >/dev/null 2>&1 && usermod %s %s || useradd %s %s", - name, modOptsStr, name, addOptsStr, name) + cmd = sprintf("id %s >/dev/null 2>&1 && %s %s %s || %s %s %s", + name, modCmd, modOptsStr, name, addCmd, addOptsStr, name) } stdout, stderr, rc, err := client.Run(ctx, cmd) diff --git a/modules_adv_test.go b/modules_adv_test.go index 5241863..c30202d 100644 --- a/modules_adv_test.go +++ b/modules_adv_test.go @@ -148,6 +148,43 @@ func TestModulesAdv_ModuleUser_Good_NoOptsUsesSimpleForm(t *testing.T) { assert.False(t, result.Failed) } +func TestModulesAdv_ModuleUser_Good_LocalModeUsesLocalCommands(t *testing.T) { + e, mock := newTestExecutorWithMock("host1") + mock.expectCommand(`id localuser >/dev/null 2>&1 && lusermod`, "", "", 0) + + result, err := moduleUserWithClient(e, mock, map[string]any{ + "name": "localuser", + "local": true, + "shell": "/bin/zsh", + "home": "/var/lib/localuser", + "append": true, + }) + + require.NoError(t, err) + assert.True(t, result.Changed) + assert.False(t, result.Failed) + assert.True(t, mock.containsSubstring("lusermod")) + assert.True(t, mock.containsSubstring("luseradd")) + assert.True(t, mock.containsSubstring("-s /bin/zsh")) + assert.True(t, mock.containsSubstring("-d /var/lib/localuser")) +} + +func TestModulesAdv_ModuleUser_Good_LocalModeRemovesLocalUser(t *testing.T) { + e, mock := newTestExecutorWithMock("host1") + mock.expectCommand(`luserdel -r localuser`, "", "", 0) + + result, err := moduleUserWithClient(e, mock, map[string]any{ + "name": "localuser", + "local": true, + "state": "absent", + }) + + require.NoError(t, err) + assert.True(t, result.Changed) + assert.False(t, result.Failed) + assert.True(t, mock.hasExecuted(`luserdel -r localuser`)) +} + func TestModulesAdv_ModuleUser_Bad_MissingName(t *testing.T) { e, _ := newTestExecutorWithMock("host1") mock := NewMockSSHClient()