ipn, ipn/ipnlocal: reduce coupling between LocalBackend/profileManager and the Windows-specific "current user" model

Ultimately, we'd like to get rid of the concept of the "current user". It is only used on Windows,
but even then it doesn't work well in multi-user and enterprise/managed Windows environments.

In this PR, we update LocalBackend and profileManager to decouple them a bit more from this obsolete concept.
This is done in a preparation for extracting ipnlocal.Extension-related interfaces and types, and using them
to implement optional features like tailscale/corp#27645, instead of continuing growing the core ipnlocal logic.

Notably, we rename (*profileManager).SetCurrentUserAndProfile() to SwitchToProfile() and change its signature
to accept an ipn.LoginProfileView instead of an ipn.ProfileID and ipn.WindowsUserID. Since we're not removing
the "current user" completely just yet, the method sets the current user to the owner of the target profile.

We also update the profileResolver callback type, which is typically implemented by LocalBackend extensions,
to return an ipn.LoginProfileView instead of ipn.ProfileID and ipn.WindowsUserID.

Updates tailscale/corp#27645
Updates tailscale/corp#18342

Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
Nick Khyl
2025-04-05 22:15:26 -05:00
committed by Nick Khyl
parent 476a4c6ff1
commit 94f4f83731
6 changed files with 207 additions and 166 deletions
+22 -19
View File
@@ -109,37 +109,39 @@ func (e *desktopSessionsExt) updateDesktopSessionState(session *desktop.Session)
// getBackgroundProfile is a [profileResolver] that works as follows:
//
// If Always-On mode is disabled, it returns no profile ("","",false).
// If Always-On mode is disabled, it returns no profile.
//
// If AlwaysOn mode is enabled, it returns the current profile unless:
// - The current user has signed out.
// - The current profile's owner has signed out.
// - Another user has a foreground (i.e. active/unlocked) session.
//
// If the current user's session runs in the background and no other user
// If the current profile owner's session runs in the background and no other user
// has a foreground session, it returns the current profile. This applies
// when a locally signed-in user locks their screen or when a remote user
// disconnects without signing out.
//
// In all other cases, it returns no profile ("","",false).
// In all other cases, it returns no profile.
//
// It is called with [LocalBackend.mu] locked.
func (e *desktopSessionsExt) getBackgroundProfile() (_ ipn.WindowsUserID, _ ipn.ProfileID, ok bool) {
func (e *desktopSessionsExt) getBackgroundProfile() ipn.LoginProfileView {
e.mu.Lock()
defer e.mu.Unlock()
if alwaysOn, _ := syspolicy.GetBoolean(syspolicy.AlwaysOn, false); !alwaysOn {
return "", "", false
// If the Always-On mode is disabled, there's no background profile
// as far as the desktop session extension is concerned.
return ipn.LoginProfileView{}
}
isCurrentUserSingedIn := false
isCurrentProfileOwnerSignedIn := false
var foregroundUIDs []ipn.WindowsUserID
for _, s := range e.id2sess {
switch uid := s.User.UserID(); uid {
case e.pm.CurrentUserID():
isCurrentUserSingedIn = true
case e.pm.CurrentProfile().LocalUserID():
isCurrentProfileOwnerSignedIn = true
if s.Status == desktop.ForegroundSession {
// Keep the current profile if the user has a foreground session.
return e.pm.CurrentUserID(), e.pm.CurrentProfile().ID(), true
return e.pm.CurrentProfile()
}
default:
if s.Status == desktop.ForegroundSession {
@@ -148,23 +150,24 @@ func (e *desktopSessionsExt) getBackgroundProfile() (_ ipn.WindowsUserID, _ ipn.
}
}
// If there's no current user (e.g., tailscaled just started), or if the current
// user has no foreground session, switch to the default profile of the first user
// with a foreground session, if any.
// If the current profile is empty and not owned by anyone (e.g., tailscaled just started),
// or if the current profile's owner has no foreground session, switch to the default profile
// of the first user with a foreground session, if any.
for _, uid := range foregroundUIDs {
if profileID := e.pm.DefaultUserProfileID(uid); profileID != "" {
return uid, profileID, true
if profile := e.pm.DefaultUserProfile(uid); profile.ID() != "" {
return profile
}
}
// If no user has a foreground session but the current user is still signed in,
// If no user has a foreground session but the current profile's owner is still signed in,
// keep the current profile even if the session is not in the foreground,
// such as when the screen is locked or a remote session is disconnected.
if len(foregroundUIDs) == 0 && isCurrentUserSingedIn {
return e.pm.CurrentUserID(), e.pm.CurrentProfile().ID(), true
if len(foregroundUIDs) == 0 && isCurrentProfileOwnerSignedIn {
return e.pm.CurrentProfile()
}
return "", "", false
// Otherwise, there's no background profile.
return ipn.LoginProfileView{}
}
// Shutdown implements [localBackendExtension].