users: refactor method for getting UID from username (#19840)

This PR refactors a helper function for getting the UID associated with
a given username to also return the GID and home directory. Also adds
unit tests on the known values of root and nobody user on Ubuntu Linux.
This commit is contained in:
Seth Hoenig
2024-01-29 13:56:30 -06:00
committed by GitHub
parent 41277f823f
commit b50b81e488
2 changed files with 41 additions and 19 deletions

View File

@@ -24,6 +24,29 @@ func Lookup(username string) (*user.User, error) {
return globalCache.GetUser(username)
}
// LookupUnix returns the UID, GID, and home directory for username or returns
// an error. ID values are int to work well with Go library functions.
//
// Will always fail on Windows and Plan 9.
func LookupUnix(username string) (int, int, string, error) {
u, err := Lookup(username)
if err != nil {
return 0, 0, "", fmt.Errorf("error looking up user %q: %w", username, err)
}
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return 0, 0, "", fmt.Errorf("error parsing uid: %w", err)
}
gid, err := strconv.Atoi(u.Gid)
if err != nil {
return 0, 0, "", fmt.Errorf("error parsing gid: %w", err)
}
return uid, gid, u.HomeDir, nil
}
// lock is used to serialize all user lookup at the process level, because
// some NSS implementations are not concurrency safe
var lock sync.Mutex
@@ -43,23 +66,6 @@ func Current() (*user.User, error) {
return user.Current()
}
// UIDforUser returns the UID for the specified username or returns an error.
//
// Will always fail on Windows and Plan 9.
func UIDforUser(username string) (int, error) {
u, err := Lookup(username)
if err != nil {
return 0, err
}
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return 0, fmt.Errorf("error parsing uid: %w", err)
}
return uid, nil
}
// WriteFileFor is like os.WriteFile except if possible it chowns the file to
// the specified user (possibly from Task.User) and sets the permissions to
// 0o600.
@@ -98,7 +104,7 @@ func WriteFileFor(path string, contents []byte, username string) error {
}
func writeFileFor(path string, contents []byte, username string) error {
uid, err := UIDforUser(username)
uid, _, _, err := LookupUnix(username)
if err != nil {
return err
}
@@ -154,7 +160,7 @@ func SocketFileFor(logger hclog.Logger, path, username string) (net.Listener, er
}
func setSocketOwner(path, username string) error {
uid, err := UIDforUser(username)
uid, _, _, err := LookupUnix(username)
if err != nil {
return err
}

View File

@@ -114,3 +114,19 @@ func TestSocketFileFor_Linux(t *testing.T) {
must.Eq(t, 0o666, int(stat.Mode().Perm()))
}
}
func TestLookupUnix_root(t *testing.T) {
uid, gid, home, err := LookupUnix("root")
must.NoError(t, err)
must.Zero(t, uid) // linux
must.Zero(t, gid) // linux
must.Eq(t, "/root", home) // ubuntu specific
}
func TestLookupUnix_nobody(t *testing.T) {
uid, gid, home, err := LookupUnix("nobody")
must.NoError(t, err)
must.Eq(t, 65534, uid) // systemd specific
must.Eq(t, 65534, gid) // systemd specific
must.Eq(t, "/nonexistent", home) // ubuntu specific
}