mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-19 05:09:06 +02:00
181 lines
5.9 KiB
Go
181 lines
5.9 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"os/user"
|
|
"path/filepath"
|
|
"runtime"
|
|
"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/client/proto"
|
|
)
|
|
|
|
// claimOwner names an owner the platform could actually hold, the way
|
|
// privilegedIdentity does for callers: a uid names nobody on Windows, where an
|
|
// owner is a SID, so a hardcoded one is refused before a test reaches what it
|
|
// is checking. The account itself need not exist, since a claim never looks one
|
|
// up.
|
|
func claimOwner(n uint32) string {
|
|
if runtime.GOOS == "windows" {
|
|
return ipcauth.OwnerPrincipalForIdentity(ipcauth.Identity{SID: fmt.Sprintf("S-1-5-21-1-2-3-%d", n)})
|
|
}
|
|
return ipcauth.OwnerPrincipalForIdentity(ipcauth.Identity{UID: n})
|
|
}
|
|
|
|
// claimTestServer points the profile manager at a temp dir holding one default
|
|
// profile, which is the profile a claim exists to settle.
|
|
func claimTestServer(t *testing.T) *Server {
|
|
t.Helper()
|
|
|
|
origDir, origPath, origActive := profilemanager.DefaultConfigPathDir, profilemanager.DefaultConfigPath, profilemanager.ActiveProfileStatePath
|
|
t.Cleanup(func() {
|
|
profilemanager.DefaultConfigPathDir = origDir
|
|
profilemanager.DefaultConfigPath = origPath
|
|
profilemanager.ActiveProfileStatePath = origActive
|
|
})
|
|
|
|
dir := t.TempDir()
|
|
profilemanager.DefaultConfigPathDir = dir
|
|
profilemanager.DefaultConfigPath = filepath.Join(dir, "default.json")
|
|
profilemanager.ActiveProfileStatePath = filepath.Join(dir, "active_profile.json")
|
|
|
|
sm := profilemanager.NewServiceManager("")
|
|
require.NoError(t, sm.CreateDefaultProfile())
|
|
|
|
srv := newTestServer()
|
|
srv.profileManager = sm
|
|
return srv
|
|
}
|
|
|
|
func TestClaimProfile_RecordsTheOwner(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
owner := claimOwner(4242)
|
|
resp, err := srv.ClaimProfile(withTarget(rootCtx(), profilemanager.DefaultConfigPath), &proto.ClaimProfileRequest{
|
|
Handle: "default",
|
|
Owner: owner,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "default", resp.GetId())
|
|
assert.Equal(t, owner, resp.GetOwner())
|
|
|
|
list, err := srv.ListProfiles(rootCtx(), &proto.ListProfilesRequest{})
|
|
require.NoError(t, err)
|
|
require.Len(t, list.GetProfiles(), 1)
|
|
assert.Equal(t, []string{owner}, list.GetProfiles()[0].GetOwners(),
|
|
"the listing has to show the owner, it is the only way to confirm a claim")
|
|
}
|
|
|
|
// The owner is not looked up, so a claim lands on a machine whose accounts do
|
|
// not exist yet. Only its shape is checked.
|
|
func TestClaimProfile_RejectsWhatWouldMatchNobody(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
owner string
|
|
}{
|
|
{"unparseable uid", "uid:abc"},
|
|
{"unknown kind", "bogus:1000"},
|
|
{"malformed sid", "sid:hello"},
|
|
{"bare number", "1000"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
_, err := srv.ClaimProfile(rootCtx(), &proto.ClaimProfileRequest{
|
|
Handle: "default",
|
|
Owner: tc.owner,
|
|
})
|
|
require.Error(t, err)
|
|
assert.Equal(t, codes.InvalidArgument, gstatus.Convert(err).Code())
|
|
|
|
list, err := srv.ListProfiles(rootCtx(), &proto.ListProfilesRequest{})
|
|
require.NoError(t, err)
|
|
assert.Empty(t, list.GetProfiles()[0].GetOwners(),
|
|
"a refused claim must leave the profile as it was")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClaimProfile_RequiresBothArguments(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
_, err := srv.ClaimProfile(rootCtx(), &proto.ClaimProfileRequest{Owner: "uid:4242"})
|
|
require.Error(t, err)
|
|
assert.Equal(t, codes.InvalidArgument, gstatus.Convert(err).Code())
|
|
|
|
_, err = srv.ClaimProfile(rootCtx(), &proto.ClaimProfileRequest{Handle: "default"})
|
|
require.Error(t, err)
|
|
assert.Equal(t, codes.InvalidArgument, gstatus.Convert(err).Code())
|
|
}
|
|
|
|
func TestClaimProfile_RefusesAnUnknownProfile(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
_, err := srv.ResolveTarget(privilegedIdentity(), "no-such-profile")
|
|
require.Error(t, err)
|
|
assert.Equal(t, codes.NotFound, gstatus.Convert(err).Code())
|
|
}
|
|
|
|
func TestClaimProfile_NeedsAnIdentifiedCaller(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
// A caller the kernel did not vouch for reaches no profile, so the gate has
|
|
// nothing to authorize and the handler is never entered.
|
|
target, err := srv.ResolveTarget(ipcauth.Identity{}, "default")
|
|
require.NoError(t, err)
|
|
assert.False(t, target.Owned, "an unattested caller must not be able to claim")
|
|
}
|
|
|
|
// A principal is taken as given. The account deliberately does not exist, which
|
|
// is the provisioning case: a machine-wide profile is configured before the
|
|
// account that will own it.
|
|
func TestClaimProfile_TakesAPrincipalWithoutResolvingIt(t *testing.T) {
|
|
for _, owner := range []string{claimOwner(4242), claimOwner(999999)} {
|
|
t.Run(owner, func(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
resp, err := srv.ClaimProfile(withTarget(rootCtx(), profilemanager.DefaultConfigPath), &proto.ClaimProfileRequest{
|
|
Handle: "default",
|
|
Owner: owner,
|
|
})
|
|
require.NoError(t, err, "a principal must never need an account lookup")
|
|
assert.Equal(t, owner, resp.GetOwner())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClaimProfile_ResolvesAnAccountName(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
u, err := user.Current()
|
|
require.NoError(t, err)
|
|
want, ok := profilemanager.PrincipalForUser(u)
|
|
require.True(t, ok)
|
|
|
|
resp, err := srv.ClaimProfile(withTarget(rootCtx(), profilemanager.DefaultConfigPath), &proto.ClaimProfileRequest{
|
|
Handle: "default",
|
|
Owner: u.Username,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, want, resp.GetOwner(),
|
|
"a name has no shortcut, only a lookup turns it into a principal")
|
|
}
|
|
|
|
func TestClaimProfile_RefusesAnUnknownAccountName(t *testing.T) {
|
|
srv := claimTestServer(t)
|
|
|
|
_, err := srv.ClaimProfile(rootCtx(), &proto.ClaimProfileRequest{
|
|
Handle: "default",
|
|
Owner: "no-such-account-here",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Equal(t, codes.InvalidArgument, gstatus.Convert(err).Code())
|
|
}
|