Skip to content

Commit 03c82a3

Browse files
cyphartianon
authored andcommitted
user: lookup: added os/user-like lookup API
This patch adds an os/user-like user lookup API, implemented in pure Go. It also has some features not present in the standard library implementation (such as group lookups). Signed-off-by: Aleksa Sarai <[email protected]> (github: cyphar)
1 parent ef3397c commit 03c82a3

3 files changed

Lines changed: 159 additions & 0 deletions

File tree

user/lookup.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package user
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"syscall"
7+
)
8+
9+
var (
10+
// The current operating system does not provide the required data for user lookups.
11+
ErrUnsupported = errors.New("user lookup: operating system does not provide passwd-formatted data")
12+
)
13+
14+
func lookupUser(filter func(u User) bool) (User, error) {
15+
// Get operating system-specific passwd reader-closer.
16+
passwd, err := GetPasswd()
17+
if err != nil {
18+
return User{}, err
19+
}
20+
defer passwd.Close()
21+
22+
// Get the users.
23+
users, err := ParsePasswdFilter(passwd, filter)
24+
if err != nil {
25+
return User{}, err
26+
}
27+
28+
// No user entries found.
29+
if len(users) == 0 {
30+
return User{}, fmt.Errorf("no matching entries in passwd file")
31+
}
32+
33+
// Assume the first entry is the "correct" one.
34+
return users[0], nil
35+
}
36+
37+
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
38+
// user cannot be found (or there is no /etc/passwd file on the filesystem),
39+
// then CurrentUser returns an error.
40+
func CurrentUser() (User, error) {
41+
return LookupUid(syscall.Getuid())
42+
}
43+
44+
// LookupUser looks up a user by their username in /etc/passwd. If the user
45+
// cannot be found (or there is no /etc/passwd file on the filesystem), then
46+
// LookupUser returns an error.
47+
func LookupUser(username string) (User, error) {
48+
return lookupUser(func(u User) bool {
49+
return u.Name == username
50+
})
51+
}
52+
53+
// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot
54+
// be found (or there is no /etc/passwd file on the filesystem), then LookupId
55+
// returns an error.
56+
func LookupUid(uid int) (User, error) {
57+
return lookupUser(func(u User) bool {
58+
return u.Uid == uid
59+
})
60+
}
61+
62+
func lookupGroup(filter func(g Group) bool) (Group, error) {
63+
// Get operating system-specific group reader-closer.
64+
group, err := GetGroup()
65+
if err != nil {
66+
return Group{}, err
67+
}
68+
defer group.Close()
69+
70+
// Get the users.
71+
groups, err := ParseGroupFilter(group, filter)
72+
if err != nil {
73+
return Group{}, err
74+
}
75+
76+
// No user entries found.
77+
if len(groups) == 0 {
78+
return Group{}, fmt.Errorf("no matching entries in group file")
79+
}
80+
81+
// Assume the first entry is the "correct" one.
82+
return groups[0], nil
83+
}
84+
85+
// CurrentGroup looks up the current user's group by their primary group id's
86+
// entry in /etc/passwd. If the group cannot be found (or there is no
87+
// /etc/group file on the filesystem), then CurrentGroup returns an error.
88+
func CurrentGroup() (Group, error) {
89+
return LookupGid(syscall.Getgid())
90+
}
91+
92+
// LookupGroup looks up a group by its name in /etc/group. If the group cannot
93+
// be found (or there is no /etc/group file on the filesystem), then LookupGroup
94+
// returns an error.
95+
func LookupGroup(groupname string) (Group, error) {
96+
return lookupGroup(func(g Group) bool {
97+
return g.Name == groupname
98+
})
99+
}
100+
101+
// LookupGid looks up a group by its group id in /etc/group. If the group cannot
102+
// be found (or there is no /etc/group file on the filesystem), then LookupGid
103+
// returns an error.
104+
func LookupGid(gid int) (Group, error) {
105+
return lookupGroup(func(g Group) bool {
106+
return g.Gid == gid
107+
})
108+
}

user/lookup_unix.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
2+
3+
package user
4+
5+
import (
6+
"io"
7+
"os"
8+
)
9+
10+
// Unix-specific path to the passwd and group formatted files.
11+
const (
12+
unixPasswdFile = "/etc/passwd"
13+
unixGroupFile = "/etc/group"
14+
)
15+
16+
func GetPasswdFile() (string, error) {
17+
return unixPasswdFile, nil
18+
}
19+
20+
func GetPasswd() (io.ReadCloser, error) {
21+
return os.Open(unixPasswdFile)
22+
}
23+
24+
func GetGroupFile() (string, error) {
25+
return unixGroupFile, nil
26+
}
27+
28+
func GetGroup() (io.ReadCloser, error) {
29+
return os.Open(unixGroupFile)
30+
}

user/lookup_unsupported.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
2+
3+
package user
4+
5+
import "io"
6+
7+
func GetPasswdFile() (string, error) {
8+
return "", ErrUnsupported
9+
}
10+
11+
func GetPasswd() (io.ReadCloser, error) {
12+
return nil, ErrUnsupported
13+
}
14+
15+
func GetGroupFile() (string, error) {
16+
return "", ErrUnsupported
17+
}
18+
19+
func GetGroup() (io.ReadCloser, error) {
20+
return nil, ErrUnsupported
21+
}

0 commit comments

Comments
 (0)