[client] Profile ownership migration (#7508)

* Implement OwnsProfile on Server

* (WIP) List profiles based on ownership by Identity

* (WIP) Migrate active_profile

* Fix status and list profiles

* Add profile stamping as active migration

* Add one-shot migration

* Only default profile fail open

* Use restricted write for config json

* Fix stale server config after stamp

* Fix OwnsProfile fallback to active profile

* Fix config concurrent reload during OwnsProfile check

* Move known check to inside stamp owner

* Update client/internal/profilemanager/service.go

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Recover from dup active profiles that cannot be resolved with username.

* Add test for already owned profile during migration

* Improve stamping of fields in the config

* Apply suggestion from @cubic-dev-ai[bot]

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Fix codespell and test comment

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
Theodor Midtlien
2026-09-17 11:20:12 +02:00
committed by GitHub
co-authored by cubic-dev-ai[bot]
parent 15ed6f8f15
commit 52b16e7a5c
19 changed files with 2050 additions and 270 deletions
+5 -2
View File
@@ -102,17 +102,20 @@ func TestSwitchProfile_ClearsJWTCache(t *testing.T) {
// switchProfileIfNeeded rather than the no-op path a nil request takes.
const target = "second"
username := "tester"
owner := unprivilegedIdentity()
_, err := profilemanager.UpdateOrCreateConfig(profilemanager.ConfigInput{
ConfigPath: filepath.Join(profilemanager.DefaultConfigPathDir, target+".json"),
ManagementURL: "https://api.netbird.io:443",
Owner: &owner,
})
require.NoError(t, err)
owner := unprivilegedIdentity()
s.jwtCache.store("token", owner, testTTL, s.jwtCache.currentGeneration())
name := target
_, err = s.SwitchProfile(ctx, &proto.SwitchProfileRequest{ProfileName: &name, Username: &username})
// The handler scopes the switch to the caller's identity, which a real
// caller gets from the daemon's transport credentials.
_, err = s.SwitchProfile(ctxWithIdentity(owner), &proto.SwitchProfileRequest{ProfileName: &name, Username: &username})
require.NoError(t, err)
active, err := s.profileManager.GetActiveProfileState()