Files
netbird/client/server/ownership_test.go
2026-07-24 15:31:57 +02:00

89 lines
3.2 KiB
Go

package server
import (
"context"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
"github.com/netbirdio/netbird/client/internal/ipcauth"
"github.com/netbirdio/netbird/client/internal/profilemanager"
"github.com/netbirdio/netbird/util"
)
// writeTargetProfile writes a profile JSON with the given ownership and returns
// a Profile handle pointing at it (Path set, so FilePath() resolves directly).
func writeTargetProfile(t *testing.T, dir, id string, owners []string, shared bool) *profilemanager.Profile {
t.Helper()
path := filepath.Join(dir, id+".json")
cfg := &profilemanager.Config{Owners: owners, Shared: shared}
require.NoError(t, util.WriteJson(context.Background(), path, cfg))
return &profilemanager.Profile{ID: profilemanager.ID(id), Name: id, Path: path}
}
func readOwners(t *testing.T, path string) ([]string, bool) {
t.Helper()
cfg, err := profilemanager.GetConfig(path)
require.NoError(t, err)
return cfg.Owners, cfg.Shared
}
func TestAuthorizeTargetProfile(t *testing.T) {
s := &Server{groupResolver: ipcauth.NewDefaultGroupResolver()}
owner := ipcauth.Identity{UID: 1000}
other := ipcauth.Identity{UID: 1001}
root := ipcauth.Identity{UID: 0}
t.Run("no identity denies", func(t *testing.T) {
p := writeTargetProfile(t, t.TempDir(), "p", []string{"uid:1000"}, false)
err := s.authorizeTargetProfile(context.Background(), p, true)
assert.Equal(t, codes.PermissionDenied, gstatus.Code(err))
})
t.Run("privileged allowed on another's profile", func(t *testing.T) {
p := writeTargetProfile(t, t.TempDir(), "p", []string{"uid:1000"}, false)
assert.NoError(t, s.authorizeTargetProfile(ctxWithIdentity(root), p, true))
})
t.Run("owner allowed", func(t *testing.T) {
p := writeTargetProfile(t, t.TempDir(), "p", []string{"uid:1000"}, false)
assert.NoError(t, s.authorizeTargetProfile(ctxWithIdentity(owner), p, true))
})
t.Run("non-owner denied", func(t *testing.T) {
p := writeTargetProfile(t, t.TempDir(), "p", []string{"uid:1000"}, false)
err := s.authorizeTargetProfile(ctxWithIdentity(other), p, true)
assert.Equal(t, codes.PermissionDenied, gstatus.Code(err))
})
t.Run("shared allows any caller", func(t *testing.T) {
p := writeTargetProfile(t, t.TempDir(), "p", nil, true)
assert.NoError(t, s.authorizeTargetProfile(ctxWithIdentity(other), p, true))
})
t.Run("unowned claim stamps owner", func(t *testing.T) {
p := writeTargetProfile(t, t.TempDir(), "p", nil, false)
require.NoError(t, s.authorizeTargetProfile(ctxWithIdentity(other), p, true))
owners, shared := readOwners(t, p.Path)
assert.Equal(t, []string{"uid:1001"}, owners)
assert.False(t, shared)
// A different caller is now locked out of the claimed profile.
err := s.authorizeTargetProfile(ctxWithIdentity(owner), p, true)
assert.Equal(t, codes.PermissionDenied, gstatus.Code(err))
})
t.Run("unowned without claim leaves profile unowned", func(t *testing.T) {
p := writeTargetProfile(t, t.TempDir(), "p", nil, false)
require.NoError(t, s.authorizeTargetProfile(ctxWithIdentity(other), p, false))
owners, _ := readOwners(t, p.Path)
assert.Empty(t, owners)
})
}