mirror of
https://github.com/golang/go
synced 2024-11-11 21:40:21 -07:00
os/user: add Go implementation of LookupGroup, LookupGroupId
If cgo is not available, parse /etc/group in Go to find the name/gid we need. This does not consult the Network Information System (NIS), /etc/nsswitch.conf or any other libc extensions to /etc/group. Fixes #18102. Change-Id: I6ae4fe0e2c899396c45cdf243d5483113932657c Reviewed-on: https://go-review.googlesource.com/33713 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
459d061c99
commit
949f95e7a4
@ -54,14 +54,6 @@ func lookupUserId(uid string) (*User, error) {
|
||||
return nil, errors.New("user: LookupId requires cgo")
|
||||
}
|
||||
|
||||
func lookupGroup(groupname string) (*Group, error) {
|
||||
return nil, errors.New("user: LookupGroup requires cgo")
|
||||
}
|
||||
|
||||
func lookupGroupId(string) (*Group, error) {
|
||||
return nil, errors.New("user: LookupGroupId requires cgo")
|
||||
}
|
||||
|
||||
func listGroups(*User) ([]string, error) {
|
||||
return nil, errors.New("user: GroupIds requires cgo")
|
||||
}
|
||||
|
99
src/os/user/lookup_unix.go
Normal file
99
src/os/user/lookup_unix.go
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// +build darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris
|
||||
// +build !cgo
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const groupFile = "/etc/group"
|
||||
|
||||
var colon = []byte{':'}
|
||||
|
||||
func init() {
|
||||
groupImplemented = false
|
||||
}
|
||||
|
||||
func findGroupId(id string, r io.Reader) (*Group, error) {
|
||||
bs := bufio.NewScanner(r)
|
||||
substr := []byte(":" + id)
|
||||
for bs.Scan() {
|
||||
lineBytes := bs.Bytes()
|
||||
if !bytes.Contains(lineBytes, substr) || bytes.Count(lineBytes, colon) < 3 {
|
||||
continue
|
||||
}
|
||||
text := strings.TrimSpace(removeComment(string(lineBytes)))
|
||||
// wheel:*:0:root
|
||||
parts := strings.SplitN(text, ":", 4)
|
||||
if len(parts) < 4 {
|
||||
continue
|
||||
}
|
||||
if parts[2] == id {
|
||||
return &Group{Name: parts[0], Gid: parts[2]}, nil
|
||||
}
|
||||
}
|
||||
if err := bs.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, UnknownGroupIdError(id)
|
||||
}
|
||||
|
||||
func findGroupName(name string, r io.Reader) (*Group, error) {
|
||||
bs := bufio.NewScanner(r)
|
||||
substr := []byte(name + ":")
|
||||
for bs.Scan() {
|
||||
lineBytes := bs.Bytes()
|
||||
if !bytes.Contains(lineBytes, substr) || bytes.Count(lineBytes, colon) < 3 {
|
||||
continue
|
||||
}
|
||||
text := strings.TrimSpace(removeComment(string(lineBytes)))
|
||||
// wheel:*:0:root
|
||||
parts := strings.SplitN(text, ":", 4)
|
||||
if len(parts) < 4 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == name && parts[2] != "" {
|
||||
return &Group{Name: parts[0], Gid: parts[2]}, nil
|
||||
}
|
||||
}
|
||||
if err := bs.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, UnknownGroupError(name)
|
||||
}
|
||||
|
||||
func lookupGroup(groupname string) (*Group, error) {
|
||||
f, err := os.Open(groupFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return findGroupName(groupname, f)
|
||||
}
|
||||
|
||||
func lookupGroupId(id string) (*Group, error) {
|
||||
f, err := os.Open(groupFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return findGroupId(id, f)
|
||||
}
|
||||
|
||||
// removeComment returns line, removing any '#' byte and any following
|
||||
// bytes.
|
||||
func removeComment(line string) string {
|
||||
if i := strings.Index(line, "#"); i != -1 {
|
||||
return line[:i]
|
||||
}
|
||||
return line
|
||||
}
|
117
src/os/user/lookup_unix_test.go
Normal file
117
src/os/user/lookup_unix_test.go
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// +build darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris
|
||||
// +build !cgo
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testGroupFile = `# See the opendirectoryd(8) man page for additional
|
||||
# information about Open Directory.
|
||||
##
|
||||
nobody:*:-2:
|
||||
nogroup:*:-1:
|
||||
wheel:*:0:root
|
||||
emptyid:*::root
|
||||
|
||||
daemon:*:1:root
|
||||
indented:*:7:
|
||||
# comment:*:4:found
|
||||
# comment:*:4:found
|
||||
kmem:*:2:root
|
||||
`
|
||||
|
||||
var groupTests = []struct {
|
||||
in string
|
||||
name string
|
||||
gid string
|
||||
}{
|
||||
{testGroupFile, "nobody", "-2"},
|
||||
{testGroupFile, "kmem", "2"},
|
||||
{testGroupFile, "notinthefile", ""},
|
||||
{testGroupFile, "comment", ""},
|
||||
{testGroupFile, "emptyid", ""},
|
||||
{testGroupFile, "indented", "7"},
|
||||
{testGroupFile, "# comment", ""},
|
||||
{"", "emptyfile", ""},
|
||||
}
|
||||
|
||||
func TestFindGroupName(t *testing.T) {
|
||||
for _, tt := range groupTests {
|
||||
got, err := findGroupName(tt.name, strings.NewReader(tt.in))
|
||||
if tt.gid == "" {
|
||||
if err == nil {
|
||||
t.Errorf("findGroupName(%s): got nil error, expected err", tt.name)
|
||||
continue
|
||||
}
|
||||
switch terr := err.(type) {
|
||||
case UnknownGroupError:
|
||||
if terr.Error() != "group: unknown group "+tt.name {
|
||||
t.Errorf("findGroupName(%s): got %v, want %v", tt.name, terr, tt.name)
|
||||
}
|
||||
default:
|
||||
t.Errorf("findGroupName(%s): got unexpected error %v", tt.name, terr)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("findGroupName(%s): got unexpected error %v", tt.name, err)
|
||||
}
|
||||
if got.Gid != tt.gid {
|
||||
t.Errorf("findGroupName(%s): got gid %v, want %s", tt.name, got.Gid, tt.gid)
|
||||
}
|
||||
if got.Name != tt.name {
|
||||
t.Errorf("findGroupName(%s): got name %s, want %s", tt.name, got.Name, tt.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var groupIdTests = []struct {
|
||||
in string
|
||||
gid string
|
||||
name string
|
||||
}{
|
||||
{testGroupFile, "-2", "nobody"},
|
||||
{testGroupFile, "2", "kmem"},
|
||||
{testGroupFile, "notinthefile", ""},
|
||||
{testGroupFile, "comment", ""},
|
||||
{testGroupFile, "7", "indented"},
|
||||
{testGroupFile, "4", ""},
|
||||
{"", "emptyfile", ""},
|
||||
}
|
||||
|
||||
func TestFindGroupId(t *testing.T) {
|
||||
for _, tt := range groupIdTests {
|
||||
got, err := findGroupId(tt.gid, strings.NewReader(tt.in))
|
||||
if tt.name == "" {
|
||||
if err == nil {
|
||||
t.Errorf("findGroupId(%s): got nil error, expected err", tt.gid)
|
||||
continue
|
||||
}
|
||||
switch terr := err.(type) {
|
||||
case UnknownGroupIdError:
|
||||
if terr.Error() != "group: unknown groupid "+tt.gid {
|
||||
t.Errorf("findGroupId(%s): got %v, want %v", tt.name, terr, tt.name)
|
||||
}
|
||||
default:
|
||||
t.Errorf("findGroupId(%s): got unexpected error %v", tt.name, terr)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("findGroupId(%s): got unexpected error %v", tt.name, err)
|
||||
}
|
||||
if got.Gid != tt.gid {
|
||||
t.Errorf("findGroupId(%s): got gid %v, want %s", tt.name, got.Gid, tt.gid)
|
||||
}
|
||||
if got.Name != tt.name {
|
||||
t.Errorf("findGroupId(%s): got name %s, want %s", tt.name, got.Name, tt.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user