Skip to content

Commit

Permalink
add os/user.Lookup
Browse files Browse the repository at this point in the history
Signed-off-by: leongross <[email protected]>
  • Loading branch information
leongross committed Apr 5, 2024
1 parent 2733e37 commit 3c925ed
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/os/user/lookup_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build !baremetal && !js && !wasip1 && !windows
// +build !baremetal,!js,!wasip1,!windows

package user

import (
"bufio"
"os"
"strings"
)

func lookupUser(username string) (*User, error) {
f, err := os.Open("/etc/passwd")
if err != nil {
return nil, err
}
defer f.Close()

// parse file format <username>:<password>:<uid>:<gid>:<gecos>:<home>:<shell>
lines := bufio.NewScanner(f)
for lines.Scan() {
line := lines.Text()
fragments := strings.Split(line, ":")

if len(fragments) < 7 {
continue
}

if fragments[0] == username {
return &User{
Uid: fragments[2],
Gid: fragments[3],
Username: fragments[0],
Name: fragments[4],
HomeDir: fragments[5],
}, nil
}
}

return nil, UnknownUserError(username)
}
16 changes: 16 additions & 0 deletions src/os/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,27 @@ type User struct {
HomeDir string
}

// UnknownUserError is returned by Lookup when a user cannot be found.
type UnknownUserError string

func (e UnknownUserError) Error() string {
return "user: unknown user " + string(e)
}

// Current returns the current user.
//
// The first call will cache the current user information.
// Subsequent calls will return the cached value and will not reflect
// changes to the current user.
// TODO: implement syscall.Getuid() and syscall.Getgid() to get the current user.
func Current() (*User, error) {
return nil, errors.New("user: Current not implemented")
}

// Lookup looks up a user by username.
//
// If the user cannot be found, the returned error is of type UnknownUserError.
// NOTE: This implementation does not support caching as the golang implementation does.
func Lookup(username string) (*User, error) {
return lookupUser(username)
}
59 changes: 59 additions & 0 deletions src/os/user_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build !baremetal && !js && !wasip1 && !windows
// +build !baremetal,!js,!wasip1,!windows

package os_test

import (
. "os/user"
"runtime"
"testing"
)

// NOTE: This test requires some users to be present in the CI environment.
// If the test fails, it may be because the users are not present.
// We can guarantee that the root user is present on all systems.
// Currently we can also guarantee that the user with UID 1001 and GID 127 is present on all systems.
func TestUserLookup(t *testing.T) {
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
t.Skip()
}

testCases := map[string]struct {
user User
wantErr bool
}{
"root": {
wantErr: false,
user: User{
Uid: "0",
Gid: "0",
Username: "root",
Name: "root",
HomeDir: "/root",
},
},
"error": {
wantErr: true,
user: User{
Uid: "1000000",
Gid: "1000000",
Username: "nonexistentuser",
Name: "nonexistentuser",
HomeDir: "/home/nonexistentuser",
},
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
user, err := Lookup(tc.user.Username)
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
t.Fatalf("Lookup(%q) = %v; want %v, got error %v", tc.user.Username, user, tc.user, err)
}
})
}
}

0 comments on commit 3c925ed

Please sign in to comment.