1
0
mirror of https://github.com/golang/go synced 2024-11-24 21:10:04 -07:00

os/user: User.GroupIds shouldn't error on users with no groups

On Windows, the User.GroupIds currently errors out if the user has no
groups. This is incorrect, as the user may not be a member of any groups
as demonstrated by the new TestGroupIdsTestUser test.

Cq-Include-Trybots: luci.golang.try:gotip-windows-amd64-longtest,gotip-windows-arm64
Change-Id: I436aa6214f2b98ef98dfb6064caec3d682b3f3d2
Reviewed-on: https://go-review.googlesource.com/c/go/+/606675
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
qmuntal 2024-08-19 15:14:23 +02:00 committed by Quim Muntal
parent af86efbe6d
commit 1b5ae45181
2 changed files with 34 additions and 14 deletions

View File

@ -151,17 +151,13 @@ func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) {
// NetUserGetLocalGroups() would return a list of LocalGroupUserInfo0
// elements which hold the names of local groups where the user participates.
// The list does not follow any sorting order.
//
// If no groups can be found for this user, NetUserGetLocalGroups() should
// always return the SID of a single group called "None", which
// also happens to be the primary group for the local user.
err = windows.NetUserGetLocalGroups(nil, q, 0, windows.LG_INCLUDE_INDIRECT, &p0, windows.MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries)
if err != nil {
return nil, err
}
defer syscall.NetApiBufferFree(p0)
if entriesRead == 0 {
return nil, fmt.Errorf("listGroupsForUsernameAndDomain: NetUserGetLocalGroups() returned an empty list for domain: %s, username: %s", domain, username)
return nil, nil
}
entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead:entriesRead]
var sids []string

View File

@ -23,12 +23,13 @@ import (
// windowsTestAcount creates a test user and returns a token for that user.
// If the user already exists, it will be deleted and recreated.
// The caller is responsible for closing the token.
func windowsTestAcount(t *testing.T) syscall.Token {
func windowsTestAcount(t *testing.T) (syscall.Token, *User) {
const testUserName = "GoStdTestUser01"
var password [33]byte
rand.Read(password[:])
// Add special chars to ensure it satisfies password requirements.
pwd := base64.StdEncoding.EncodeToString(password[:]) + "_-As@!%*(1)4#2"
name, err := syscall.UTF16PtrFromString("GoStdTestUser01")
name, err := syscall.UTF16PtrFromString(testUserName)
if err != nil {
t.Fatal(err)
}
@ -57,6 +58,13 @@ func windowsTestAcount(t *testing.T) syscall.Token {
} else if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err = windows.NetUserDel(nil, name); err != nil {
if !errors.Is(err, windows.NERR_UserNotFound) {
t.Fatal(err)
}
}
})
domain, err := syscall.UTF16PtrFromString(".")
if err != nil {
t.Fatal(err)
@ -69,13 +77,12 @@ func windowsTestAcount(t *testing.T) syscall.Token {
}
t.Cleanup(func() {
token.Close()
if err = windows.NetUserDel(nil, name); err != nil {
if !errors.Is(err, windows.NERR_UserNotFound) {
})
usr, err := Lookup(testUserName)
if err != nil {
t.Fatal(err)
}
}
})
return token
return token, usr
}
func TestImpersonatedSelf(t *testing.T) {
@ -127,7 +134,7 @@ func TestImpersonated(t *testing.T) {
}
// Create a test user and log in as that user.
token := windowsTestAcount(t)
token, _ := windowsTestAcount(t)
// Impersonate the test user.
if err = windows.ImpersonateLoggedOnUser(token); err != nil {
@ -178,3 +185,20 @@ func TestCurrentNetapi32(t *testing.T) {
t.Fatalf("%v\n%s", err, out)
}
}
func TestGroupIdsTestUser(t *testing.T) {
// Create a test user and log in as that user.
_, user := windowsTestAcount(t)
gids, err := user.GroupIds()
if err != nil {
t.Fatal(err)
}
if err != nil {
t.Fatalf("%+v.GroupIds(): %v", user, err)
}
if !containsID(gids, user.Gid) {
t.Errorf("%+v.GroupIds() = %v; does not contain user GID %s", user, gids, user.Gid)
}
}