Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix user removed from local groups #644

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ UserByName:
UserToBroker: {}
UserToGroups:
"1648262143": '{"UID":1648262143,"GIDs":[1648262143,1946747284]}'
UserToLocalGroups:
"1648262143": "null"
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ UserByName:
UserToBroker: {}
UserToGroups:
"1648262143": '{"UID":1648262143,"GIDs":[1648262143,1946747284]}'
UserToLocalGroups:
"1648262143": "null"
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups:
"1042855937": '["localgroup1","localgroup3"]'
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ UserByName:
UserToBroker: {}
UserToGroups:
"1556535091": '{"UID":1556535091,"GIDs":[1556535091,1369382419]}'
UserToLocalGroups:
"1556535091": "null"
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ UserByName:
UserToBroker: {}
UserToGroups:
"71705": '{"UID":71705,"GIDs":[71705,1795458232]}'
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ UserByName:
UserToBroker: {}
UserToGroups:
"1797931382": '{"UID":1797931382,"GIDs":[1797931382,1840530284]}'
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ UserByName:
UserToBroker: {}
UserToGroups:
"1127066031": '{"UID":1127066031,"GIDs":[1127066031,1946747284]}'
UserToLocalGroups:
"1127066031": "null"
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ UserByName:
UserToBroker: {}
UserToGroups:
"1569396774": '{"UID":1569396774,"GIDs":[1569396774,1369382419]}'
UserToLocalGroups:
"1569396774": "null"
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ UserToBroker:
UserToGroups:
"77777": '{"UID":77777,"GIDs":[88888]}'
"1714308795": '{"UID":1714308795,"GIDs":[1714308795,88888]}'
UserToLocalGroups:
"1714308795": "null"
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ UserByName:
UserToBroker: {}
UserToGroups:
"1370830640": '{"UID":1370830640,"GIDs":[1370830640,1602050681]}'
UserToLocalGroups:
"1370830640": '["localgroup1","localgroup3"]'
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ wait: ""
entry: ""
content: ""
code: ""
rendersqrcode: null
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ wait: ""
entry: entry_type
content: ""
code: ""
rendersqrcode: null
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ UserToGroups:
"3333": '{"UID":3333,"GIDs":[33333,99999]}'
"4444": '{"UID":4444,"GIDs":[44444,99999]}'
"5555": '{"UID":5555,"GIDs":[55555,99999]}'
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ UserToGroups:
"3333": '{"UID":3333,"GIDs":[33333,99999]}'
"4444": '{"UID":4444,"GIDs":[44444,99999]}'
"5555": '{"UID":5555,"GIDs":[55555,99999]}'
UserToLocalGroups: {}
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ UserToGroups:
"3333": '{"UID":3333,"GIDs":[33333,99999]}'
"4444": '{"UID":4444,"GIDs":[44444,99999]}'
"5555": '{"UID":5555,"GIDs":[55555,99999]}'
UserToLocalGroups: {}
34 changes: 34 additions & 0 deletions internal/sliceutils/sliceutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Package sliceutils provides utility functions for slices.
package sliceutils

// Difference returns a slice with the elements that are in a but not in b.
func Difference[T comparable](a, b []T) []T {
setB := make(map[T]struct{}, len(b))
for _, item := range b {
setB[item] = struct{}{}
}

var diff []T
for _, item := range a {
if _, found := setB[item]; !found {
diff = append(diff, item)
}
}
return diff
}

// Intersection returns a slice with the elements that are in both a and b.
func Intersection[T comparable](a, b []T) []T {
setB := make(map[T]struct{}, len(b))
for _, item := range b {
setB[item] = struct{}{}
}

var intersection []T
for _, item := range a {
if _, found := setB[item]; found {
intersection = append(intersection, item)
}
}
return intersection
}
84 changes: 84 additions & 0 deletions internal/sliceutils/sliceutils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package sliceutils_test

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/ubuntu/authd/internal/sliceutils"
)

func TestDifference(t *testing.T) {
t.Parallel()

tests := map[string]struct {
a, b, want []int
}{
"test difference between two slices": {
a: []int{1, 2, 3, 4, 5},
b: []int{3, 4, 5, 6, 7},
want: []int{1, 2},
},
"test difference between an empty slice and a non-empty slice": {
a: []int{},
b: []int{3, 4, 5, 6, 7},
want: []int(nil),
},
"test difference between a non-empty slice and an empty slice": {
a: []int{1, 2, 3, 4, 5},
b: []int{},
want: []int{1, 2, 3, 4, 5},
},
"test difference between two empty slices": {
a: []int{},
b: []int{},
want: []int(nil),
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()

got := sliceutils.Difference(tc.a, tc.b)
require.Equal(t, tc.want, got)
})
}
}

func TestIntersection(t *testing.T) {
t.Parallel()

tests := map[string]struct {
a, b, want []int
}{
"test intersection between two slices": {
a: []int{1, 2, 3, 4, 5},
b: []int{3, 4, 5, 6, 7},
want: []int{3, 4, 5},
},
"test intersection between an empty slice and a non-empty slice": {
a: []int{},
b: []int{3, 4, 5, 6, 7},
want: []int(nil),
},
"test intersection between a non-empty slice and an empty slice": {
a: []int{1, 2, 3, 4, 5},
b: []int{},
want: []int(nil),
},
"test intersection between two empty slices": {
a: []int{},
b: []int{},
want: []int(nil),
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()

got := sliceutils.Intersection(tc.a, tc.b)
require.Equal(t, tc.want, got)
})
}
}
17 changes: 9 additions & 8 deletions internal/users/cache/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,22 @@ var (
)

const (
userByNameBucketName = "UserByName"
userByIDBucketName = "UserByID"
groupByNameBucketName = "GroupByName"
groupByIDBucketName = "GroupByID"
userToGroupsBucketName = "UserToGroups"
groupToUsersBucketName = "GroupToUsers"
userToBrokerBucketName = "UserToBroker"
userByNameBucketName = "UserByName"
userByIDBucketName = "UserByID"
groupByNameBucketName = "GroupByName"
groupByIDBucketName = "GroupByID"
userToGroupsBucketName = "UserToGroups"
groupToUsersBucketName = "GroupToUsers"
userToBrokerBucketName = "UserToBroker"
userToLocalGroupsBucketName = "UserToLocalGroups"
)

var (
allBuckets = [][]byte{
[]byte(userByNameBucketName), []byte(userByIDBucketName),
[]byte(groupByNameBucketName), []byte(groupByIDBucketName),
[]byte(userToGroupsBucketName), []byte(groupToUsersBucketName),
[]byte(userToBrokerBucketName),
[]byte(userToBrokerBucketName), []byte(userToLocalGroupsBucketName),
}
)

Expand Down
10 changes: 6 additions & 4 deletions internal/users/cache/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,10 @@ func TestUpdateUserEntry(t *testing.T) {
}

tests := map[string]struct {
userCase string
groupCases []string
dbFile string
userCase string
groupCases []string
localGroups []string
dbFile string

wantErr bool
}{
Expand All @@ -176,6 +177,7 @@ func TestUpdateUserEntry(t *testing.T) {
"Update user by adding a new group": {groupCases: []string{"group1", "group2"}, dbFile: "one_user_and_group"},
"Update user by adding a new default group": {groupCases: []string{"group2", "group1"}, dbFile: "one_user_and_group"},
"Remove group from user": {groupCases: []string{"group2"}, dbFile: "one_user_and_group"},
"Update user by adding a new local group": {localGroups: []string{"localgroup1"}, dbFile: "one_user_and_group"},

// Multi users handling
"Update only user even if we have multiple of them": {dbFile: "multiple_users_and_groups"},
Expand Down Expand Up @@ -219,7 +221,7 @@ func TestUpdateUserEntry(t *testing.T) {
}
user.GID = groups[0].GID

err := c.UpdateUserEntry(user, groups)
err := c.UpdateUserEntry(user, groups, tc.localGroups)
if tc.wantErr {
require.Error(t, err, "UpdateFromUserInfo should return an error but didn't")
return
Expand Down
1 change: 1 addition & 0 deletions internal/users/cache/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func (c *Cache) DeleteUser(uid uint32) error {
// deleteUserFromGroup removes the uid from the group.
// If the group is empty after the uid gets removed, the group is deleted from the database.
func deleteUserFromGroup(buckets map[string]bucketWithName, uid, gid uint32) error {
log.Debugf(context.TODO(), "Removing user %d from group %d", uid, gid)
groupToUsers, err := getFromBucket[groupToUsersDB](buckets[groupToUsersBucketName], gid)
if err != nil && !errors.Is(err, NoDataFoundError{}) {
return err
Expand Down
68 changes: 68 additions & 0 deletions internal/users/cache/getgroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,74 @@ func (c *Cache) GroupByName(name string) (GroupDB, error) {
return getGroup(c, groupByNameBucketName, name)
}

// UserGroups returns all groups for a given user or an error if the database is corrupted or no entry was found.
func (c *Cache) UserGroups(uid uint32) ([]GroupDB, error) {
c.mu.RLock()
defer c.mu.RUnlock()
var groups []GroupDB
err := c.db.View(func(tx *bbolt.Tx) error {
buckets, err := getAllBuckets(tx)
if err != nil {
return err
}

// Get group ids for the user.
groupsForUser, err := getFromBucket[userToGroupsDB](buckets[userToGroupsBucketName], uid)
if err != nil {
return err
}

for _, gid := range groupsForUser.GIDs {
// we should always get an entry
g, err := getFromBucket[groupDB](buckets[groupByIDBucketName], gid)
if err != nil {
return err
}

// Get user names in the group.
users, err := getUsersInGroup(buckets, gid)
if err != nil {
return err
}

groups = append(groups, NewGroupDB(g.Name, g.GID, users))
}
return nil
})

if err != nil {
return nil, err
}

return groups, nil
}

// UserLocalGroups returns all local groups for a given user or an error if the database is corrupted or no entry was found.
func (c *Cache) UserLocalGroups(uid uint32) ([]string, error) {
c.mu.RLock()
defer c.mu.RUnlock()
var localGroups []string
err := c.db.View(func(tx *bbolt.Tx) error {
buckets, err := getAllBuckets(tx)
if err != nil {
return err
}

localGroups, err = getFromBucket[[]string](buckets[userToLocalGroupsBucketName], uid)
if err != nil {
return err
}

return nil
})

if err != nil {
return nil, err
}

return localGroups, nil
}

// AllGroups returns all groups or an error if the database is corrupted.
func (c *Cache) AllGroups() (all []GroupDB, err error) {
c.mu.RLock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ UserByID: {}
UserByName: {}
UserToBroker: {}
UserToGroups: {}
UserToLocalGroups: {}
Loading
Loading