mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-19 19:38:40 +00:00
ipn: generate LoginProfileView and use it instead of *LoginProfile where appropriate
Conventionally, we use views (e.g., ipn.PrefsView, tailcfg.NodeView, etc.) when dealing with structs that shouldn't be mutated. However, ipn.LoginProfile has been an exception so far, with a mix of passing and returning LoginProfile by reference (allowing accidental mutations) and by value (which is wasteful, given its current size of 192 bytes). In this PR, we generate an ipn.LoginProfileView and use it instead of passing/returning LoginProfiles by mutable reference or copying them when passing/returning by value. Now, LoginProfiles can only be mutated by (*profileManager).setProfilePrefs. Updates #14823 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
parent
7d5fe13d27
commit
4e7f4086b2
@ -1,7 +1,7 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:generate go run tailscale.com/cmd/viewer -type=Prefs,ServeConfig,ServiceConfig,TCPPortHandler,HTTPHandler,WebServerConfig
|
||||
//go:generate go run tailscale.com/cmd/viewer -type=LoginProfile,Prefs,ServeConfig,ServiceConfig,TCPPortHandler,HTTPHandler,WebServerConfig
|
||||
|
||||
// Package ipn implements the interactions between the Tailscale cloud
|
||||
// control plane and the local network stack.
|
||||
|
@ -17,6 +17,29 @@ import (
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
||||
// Clone makes a deep copy of LoginProfile.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *LoginProfile) Clone() *LoginProfile {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
dst := new(LoginProfile)
|
||||
*dst = *src
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _LoginProfileCloneNeedsRegeneration = LoginProfile(struct {
|
||||
ID ProfileID
|
||||
Name string
|
||||
NetworkProfile NetworkProfile
|
||||
Key StateKey
|
||||
UserProfile tailcfg.UserProfile
|
||||
NodeID tailcfg.StableNodeID
|
||||
LocalUserID WindowsUserID
|
||||
ControlURL string
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Prefs.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Prefs) Clone() *Prefs {
|
||||
|
@ -18,7 +18,73 @@ import (
|
||||
"tailscale.com/types/views"
|
||||
)
|
||||
|
||||
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=Prefs,ServeConfig,ServiceConfig,TCPPortHandler,HTTPHandler,WebServerConfig
|
||||
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=LoginProfile,Prefs,ServeConfig,ServiceConfig,TCPPortHandler,HTTPHandler,WebServerConfig
|
||||
|
||||
// View returns a read-only view of LoginProfile.
|
||||
func (p *LoginProfile) View() LoginProfileView {
|
||||
return LoginProfileView{ж: p}
|
||||
}
|
||||
|
||||
// LoginProfileView provides a read-only view over LoginProfile.
|
||||
//
|
||||
// Its methods should only be called if `Valid()` returns true.
|
||||
type LoginProfileView struct {
|
||||
// ж is the underlying mutable value, named with a hard-to-type
|
||||
// character that looks pointy like a pointer.
|
||||
// It is named distinctively to make you think of how dangerous it is to escape
|
||||
// to callers. You must not let callers be able to mutate it.
|
||||
ж *LoginProfile
|
||||
}
|
||||
|
||||
// Valid reports whether v's underlying value is non-nil.
|
||||
func (v LoginProfileView) Valid() bool { return v.ж != nil }
|
||||
|
||||
// AsStruct returns a clone of the underlying value which aliases no memory with
|
||||
// the original.
|
||||
func (v LoginProfileView) AsStruct() *LoginProfile {
|
||||
if v.ж == nil {
|
||||
return nil
|
||||
}
|
||||
return v.ж.Clone()
|
||||
}
|
||||
|
||||
func (v LoginProfileView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
|
||||
|
||||
func (v *LoginProfileView) UnmarshalJSON(b []byte) error {
|
||||
if v.ж != nil {
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
var x LoginProfile
|
||||
if err := json.Unmarshal(b, &x); err != nil {
|
||||
return err
|
||||
}
|
||||
v.ж = &x
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v LoginProfileView) ID() ProfileID { return v.ж.ID }
|
||||
func (v LoginProfileView) Name() string { return v.ж.Name }
|
||||
func (v LoginProfileView) NetworkProfile() NetworkProfile { return v.ж.NetworkProfile }
|
||||
func (v LoginProfileView) Key() StateKey { return v.ж.Key }
|
||||
func (v LoginProfileView) UserProfile() tailcfg.UserProfile { return v.ж.UserProfile }
|
||||
func (v LoginProfileView) NodeID() tailcfg.StableNodeID { return v.ж.NodeID }
|
||||
func (v LoginProfileView) LocalUserID() WindowsUserID { return v.ж.LocalUserID }
|
||||
func (v LoginProfileView) ControlURL() string { return v.ж.ControlURL }
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _LoginProfileViewNeedsRegeneration = LoginProfile(struct {
|
||||
ID ProfileID
|
||||
Name string
|
||||
NetworkProfile NetworkProfile
|
||||
Key StateKey
|
||||
UserProfile tailcfg.UserProfile
|
||||
NodeID tailcfg.StableNodeID
|
||||
LocalUserID WindowsUserID
|
||||
ControlURL string
|
||||
}{})
|
||||
|
||||
// View returns a read-only view of Prefs.
|
||||
func (p *Prefs) View() PrefsView {
|
||||
|
@ -4045,7 +4045,7 @@ func (b *LocalBackend) checkProfileNameLocked(p *ipn.Prefs) error {
|
||||
// No profile with that name exists. That's fine.
|
||||
return nil
|
||||
}
|
||||
if id != b.pm.CurrentProfile().ID {
|
||||
if id != b.pm.CurrentProfile().ID() {
|
||||
// Name is already in use by another profile.
|
||||
return fmt.Errorf("profile name %q already in use", p.ProfileName)
|
||||
}
|
||||
@ -4127,7 +4127,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(newp *ipn.Prefs, unlock unlockOnce)
|
||||
}
|
||||
|
||||
prefs := newp.View()
|
||||
np := b.pm.CurrentProfile().NetworkProfile
|
||||
np := b.pm.CurrentProfile().NetworkProfile()
|
||||
if netMap != nil {
|
||||
np = ipn.NetworkProfile{
|
||||
MagicDNSName: b.netMap.MagicDNSSuffix(),
|
||||
@ -5663,7 +5663,7 @@ func (b *LocalBackend) Logout(ctx context.Context) error {
|
||||
unlock = b.lockAndGetUnlock()
|
||||
defer unlock()
|
||||
|
||||
if err := b.pm.DeleteProfile(profile.ID); err != nil {
|
||||
if err := b.pm.DeleteProfile(profile.ID()); err != nil {
|
||||
b.logf("error deleting profile: %v", err)
|
||||
return err
|
||||
}
|
||||
@ -6039,7 +6039,7 @@ func (b *LocalBackend) setDebugLogsByCapabilityLocked(nm *netmap.NetworkMap) {
|
||||
// the method to only run the reset-logic and not reload the store from memory to ensure
|
||||
// foreground sessions are not removed if they are not saved on disk.
|
||||
func (b *LocalBackend) reloadServeConfigLocked(prefs ipn.PrefsView) {
|
||||
if b.netMap == nil || !b.netMap.SelfNode.Valid() || !prefs.Valid() || b.pm.CurrentProfile().ID == "" {
|
||||
if b.netMap == nil || !b.netMap.SelfNode.Valid() || !prefs.Valid() || b.pm.CurrentProfile().ID() == "" {
|
||||
// We're not logged in, so we don't have a profile.
|
||||
// Don't try to load the serve config.
|
||||
b.lastServeConfJSON = mem.B(nil)
|
||||
@ -6047,7 +6047,7 @@ func (b *LocalBackend) reloadServeConfigLocked(prefs ipn.PrefsView) {
|
||||
return
|
||||
}
|
||||
|
||||
confKey := ipn.ServeConfigKey(b.pm.CurrentProfile().ID)
|
||||
confKey := ipn.ServeConfigKey(b.pm.CurrentProfile().ID())
|
||||
// TODO(maisem,bradfitz): prevent reading the config from disk
|
||||
// if the profile has not changed.
|
||||
confj, err := b.store.ReadState(confKey)
|
||||
@ -7000,7 +7000,7 @@ func (b *LocalBackend) ShouldInterceptVIPServiceTCPPort(ap netip.AddrPort) bool
|
||||
// It will restart the backend on success.
|
||||
// If the profile is not known, it returns an errProfileNotFound.
|
||||
func (b *LocalBackend) SwitchProfile(profile ipn.ProfileID) error {
|
||||
if b.CurrentProfile().ID == profile {
|
||||
if b.CurrentProfile().ID() == profile {
|
||||
return nil
|
||||
}
|
||||
unlock := b.lockAndGetUnlock()
|
||||
@ -7023,12 +7023,12 @@ func (b *LocalBackend) SwitchProfile(profile ipn.ProfileID) error {
|
||||
|
||||
func (b *LocalBackend) initTKALocked() error {
|
||||
cp := b.pm.CurrentProfile()
|
||||
if cp.ID == "" {
|
||||
if cp.ID() == "" {
|
||||
b.tka = nil
|
||||
return nil
|
||||
}
|
||||
if b.tka != nil {
|
||||
if b.tka.profile == cp.ID {
|
||||
if b.tka.profile == cp.ID() {
|
||||
// Already initialized.
|
||||
return nil
|
||||
}
|
||||
@ -7058,7 +7058,7 @@ func (b *LocalBackend) initTKALocked() error {
|
||||
}
|
||||
|
||||
b.tka = &tkaState{
|
||||
profile: cp.ID,
|
||||
profile: cp.ID(),
|
||||
authority: authority,
|
||||
storage: storage,
|
||||
}
|
||||
@ -7111,7 +7111,7 @@ func (b *LocalBackend) DeleteProfile(p ipn.ProfileID) error {
|
||||
unlock := b.lockAndGetUnlock()
|
||||
defer unlock()
|
||||
|
||||
needToRestart := b.pm.CurrentProfile().ID == p
|
||||
needToRestart := b.pm.CurrentProfile().ID() == p
|
||||
if err := b.pm.DeleteProfile(p); err != nil {
|
||||
if err == errProfileNotFound {
|
||||
return nil
|
||||
@ -7126,7 +7126,7 @@ func (b *LocalBackend) DeleteProfile(p ipn.ProfileID) error {
|
||||
|
||||
// CurrentProfile returns the current LoginProfile.
|
||||
// The value may be zero if the profile is not persisted.
|
||||
func (b *LocalBackend) CurrentProfile() ipn.LoginProfile {
|
||||
func (b *LocalBackend) CurrentProfile() ipn.LoginProfileView {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.pm.CurrentProfile()
|
||||
@ -7147,7 +7147,7 @@ func (b *LocalBackend) NewProfile() error {
|
||||
}
|
||||
|
||||
// ListProfiles returns a list of all LoginProfiles.
|
||||
func (b *LocalBackend) ListProfiles() []ipn.LoginProfile {
|
||||
func (b *LocalBackend) ListProfiles() []ipn.LoginProfileView {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.pm.Profiles()
|
||||
@ -7353,7 +7353,7 @@ func (b *LocalBackend) UnadvertiseRoute(toRemove ...netip.Prefix) error {
|
||||
|
||||
// namespace a key with the profile manager's current profile key, if any
|
||||
func namespaceKeyForCurrentProfile(pm *profileManager, key ipn.StateKey) ipn.StateKey {
|
||||
return pm.CurrentProfile().Key + "||" + key
|
||||
return pm.CurrentProfile().Key() + "||" + key
|
||||
}
|
||||
|
||||
const routeInfoStateStoreKey ipn.StateKey = "_routeInfo"
|
||||
@ -7361,7 +7361,7 @@ const routeInfoStateStoreKey ipn.StateKey = "_routeInfo"
|
||||
func (b *LocalBackend) storeRouteInfo(ri *appc.RouteInfo) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.pm.CurrentProfile().ID == "" {
|
||||
if b.pm.CurrentProfile().ID() == "" {
|
||||
return nil
|
||||
}
|
||||
key := namespaceKeyForCurrentProfile(b.pm, routeInfoStateStoreKey)
|
||||
@ -7373,7 +7373,7 @@ func (b *LocalBackend) storeRouteInfo(ri *appc.RouteInfo) error {
|
||||
}
|
||||
|
||||
func (b *LocalBackend) readRouteInfoLocked() (*appc.RouteInfo, error) {
|
||||
if b.pm.CurrentProfile().ID == "" {
|
||||
if b.pm.CurrentProfile().ID() == "" {
|
||||
return &appc.RouteInfo{}, nil
|
||||
}
|
||||
key := namespaceKeyForCurrentProfile(b.pm, routeInfoStateStoreKey)
|
||||
|
@ -4087,9 +4087,9 @@ func TestReadWriteRouteInfo(t *testing.T) {
|
||||
b := newTestBackend(t)
|
||||
prof1 := ipn.LoginProfile{ID: "id1", Key: "key1"}
|
||||
prof2 := ipn.LoginProfile{ID: "id2", Key: "key2"}
|
||||
b.pm.knownProfiles["id1"] = &prof1
|
||||
b.pm.knownProfiles["id2"] = &prof2
|
||||
b.pm.currentProfile = &prof1
|
||||
b.pm.knownProfiles["id1"] = prof1.View()
|
||||
b.pm.knownProfiles["id2"] = prof2.View()
|
||||
b.pm.currentProfile = prof1.View()
|
||||
|
||||
// set up routeInfo
|
||||
ri1 := &appc.RouteInfo{}
|
||||
|
@ -407,7 +407,7 @@ func (b *LocalBackend) tkaApplyDisablementLocked(secret []byte) error {
|
||||
//
|
||||
// b.mu must be held.
|
||||
func (b *LocalBackend) chonkPathLocked() string {
|
||||
return filepath.Join(b.TailscaleVarRoot(), "tka-profiles", string(b.pm.CurrentProfile().ID))
|
||||
return filepath.Join(b.TailscaleVarRoot(), "tka-profiles", string(b.pm.CurrentProfile().ID()))
|
||||
}
|
||||
|
||||
// tkaBootstrapFromGenesisLocked initializes the local (on-disk) state of the
|
||||
@ -455,7 +455,7 @@ func (b *LocalBackend) tkaBootstrapFromGenesisLocked(g tkatype.MarshaledAUM, per
|
||||
}
|
||||
|
||||
b.tka = &tkaState{
|
||||
profile: b.pm.CurrentProfile().ID,
|
||||
profile: b.pm.CurrentProfile().ID(),
|
||||
authority: authority,
|
||||
storage: chonk,
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ func TestTKADisablementFlow(t *testing.T) {
|
||||
}).View(), ipn.NetworkProfile{}))
|
||||
|
||||
temp := t.TempDir()
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID))
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID()))
|
||||
os.Mkdir(tkaPath, 0755)
|
||||
chonk, err := tka.ChonkDir(tkaPath)
|
||||
if err != nil {
|
||||
@ -410,7 +410,7 @@ func TestTKASync(t *testing.T) {
|
||||
}
|
||||
|
||||
temp := t.TempDir()
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID))
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID()))
|
||||
os.Mkdir(tkaPath, 0755)
|
||||
// Setup the TKA authority on the node.
|
||||
nodeStorage, err := tka.ChonkDir(tkaPath)
|
||||
@ -710,7 +710,7 @@ func TestTKADisable(t *testing.T) {
|
||||
}).View(), ipn.NetworkProfile{}))
|
||||
|
||||
temp := t.TempDir()
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID))
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID()))
|
||||
os.Mkdir(tkaPath, 0755)
|
||||
key := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
||||
chonk, err := tka.ChonkDir(tkaPath)
|
||||
@ -770,7 +770,7 @@ func TestTKADisable(t *testing.T) {
|
||||
ccAuto: cc,
|
||||
logf: t.Logf,
|
||||
tka: &tkaState{
|
||||
profile: pm.CurrentProfile().ID,
|
||||
profile: pm.CurrentProfile().ID(),
|
||||
authority: authority,
|
||||
storage: chonk,
|
||||
},
|
||||
@ -805,7 +805,7 @@ func TestTKASign(t *testing.T) {
|
||||
key := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
||||
|
||||
temp := t.TempDir()
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID))
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID()))
|
||||
os.Mkdir(tkaPath, 0755)
|
||||
chonk, err := tka.ChonkDir(tkaPath)
|
||||
if err != nil {
|
||||
@ -890,7 +890,7 @@ func TestTKAForceDisable(t *testing.T) {
|
||||
}).View(), ipn.NetworkProfile{}))
|
||||
|
||||
temp := t.TempDir()
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID))
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID()))
|
||||
os.Mkdir(tkaPath, 0755)
|
||||
chonk, err := tka.ChonkDir(tkaPath)
|
||||
if err != nil {
|
||||
@ -989,7 +989,7 @@ func TestTKAAffectedSigs(t *testing.T) {
|
||||
tkaKey := tka.Key{Kind: tka.Key25519, Public: nlPriv.Public().Verifier(), Votes: 2}
|
||||
|
||||
temp := t.TempDir()
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID))
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID()))
|
||||
os.Mkdir(tkaPath, 0755)
|
||||
chonk, err := tka.ChonkDir(tkaPath)
|
||||
if err != nil {
|
||||
@ -1124,7 +1124,7 @@ func TestTKARecoverCompromisedKeyFlow(t *testing.T) {
|
||||
compromisedKey := tka.Key{Kind: tka.Key25519, Public: compromisedPriv.Public().Verifier(), Votes: 1}
|
||||
|
||||
temp := t.TempDir()
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID))
|
||||
tkaPath := filepath.Join(temp, "tka-profile", string(pm.CurrentProfile().ID()))
|
||||
os.Mkdir(tkaPath, 0755)
|
||||
chonk, err := tka.ChonkDir(tkaPath)
|
||||
if err != nil {
|
||||
|
@ -35,9 +35,9 @@ type profileManager struct {
|
||||
health *health.Tracker
|
||||
|
||||
currentUserID ipn.WindowsUserID
|
||||
knownProfiles map[ipn.ProfileID]*ipn.LoginProfile // always non-nil
|
||||
currentProfile *ipn.LoginProfile // always non-nil
|
||||
prefs ipn.PrefsView // always Valid.
|
||||
knownProfiles map[ipn.ProfileID]ipn.LoginProfileView // always non-nil
|
||||
currentProfile ipn.LoginProfileView // always Valid.
|
||||
prefs ipn.PrefsView // always Valid.
|
||||
}
|
||||
|
||||
func (pm *profileManager) dlogf(format string, args ...any) {
|
||||
@ -89,7 +89,7 @@ func (pm *profileManager) DefaultUserProfileID(uid ipn.WindowsUserID) ipn.Profil
|
||||
pm.dlogf("DefaultUserProfileID: windows: migrating from legacy preferences")
|
||||
profile, err := pm.migrateFromLegacyPrefs(uid, false)
|
||||
if err == nil {
|
||||
return profile.ID
|
||||
return profile.ID()
|
||||
}
|
||||
pm.logf("failed to migrate from legacy preferences: %v", err)
|
||||
}
|
||||
@ -98,17 +98,17 @@ func (pm *profileManager) DefaultUserProfileID(uid ipn.WindowsUserID) ipn.Profil
|
||||
|
||||
pk := ipn.StateKey(string(b))
|
||||
prof := pm.findProfileByKey(pk)
|
||||
if prof == nil {
|
||||
if !prof.Valid() {
|
||||
pm.dlogf("DefaultUserProfileID: no profile found for key: %q", pk)
|
||||
return ""
|
||||
}
|
||||
return prof.ID
|
||||
return prof.ID()
|
||||
}
|
||||
|
||||
// checkProfileAccess returns an [errProfileAccessDenied] if the current user
|
||||
// does not have access to the specified profile.
|
||||
func (pm *profileManager) checkProfileAccess(profile *ipn.LoginProfile) error {
|
||||
if pm.currentUserID != "" && profile.LocalUserID != pm.currentUserID {
|
||||
func (pm *profileManager) checkProfileAccess(profile ipn.LoginProfileView) error {
|
||||
if pm.currentUserID != "" && profile.LocalUserID() != pm.currentUserID {
|
||||
return errProfileAccessDenied
|
||||
}
|
||||
return nil
|
||||
@ -116,21 +116,21 @@ func (pm *profileManager) checkProfileAccess(profile *ipn.LoginProfile) error {
|
||||
|
||||
// allProfiles returns all profiles accessible to the current user.
|
||||
// The returned profiles are sorted by Name.
|
||||
func (pm *profileManager) allProfiles() (out []*ipn.LoginProfile) {
|
||||
func (pm *profileManager) allProfiles() (out []ipn.LoginProfileView) {
|
||||
for _, p := range pm.knownProfiles {
|
||||
if pm.checkProfileAccess(p) == nil {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
slices.SortFunc(out, func(a, b *ipn.LoginProfile) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
slices.SortFunc(out, func(a, b ipn.LoginProfileView) int {
|
||||
return cmp.Compare(a.Name(), b.Name())
|
||||
})
|
||||
return out
|
||||
}
|
||||
|
||||
// matchingProfiles is like [profileManager.allProfiles], but returns only profiles
|
||||
// matching the given predicate.
|
||||
func (pm *profileManager) matchingProfiles(f func(*ipn.LoginProfile) bool) (out []*ipn.LoginProfile) {
|
||||
func (pm *profileManager) matchingProfiles(f func(ipn.LoginProfileView) bool) (out []ipn.LoginProfileView) {
|
||||
all := pm.allProfiles()
|
||||
out = all[:0]
|
||||
for _, p := range all {
|
||||
@ -144,11 +144,11 @@ func (pm *profileManager) matchingProfiles(f func(*ipn.LoginProfile) bool) (out
|
||||
// findMatchingProfiles returns all profiles accessible to the current user
|
||||
// that represent the same node/user as prefs.
|
||||
// The returned profiles are sorted by Name.
|
||||
func (pm *profileManager) findMatchingProfiles(prefs ipn.PrefsView) []*ipn.LoginProfile {
|
||||
return pm.matchingProfiles(func(p *ipn.LoginProfile) bool {
|
||||
return p.ControlURL == prefs.ControlURL() &&
|
||||
(p.UserProfile.ID == prefs.Persist().UserProfile().ID ||
|
||||
p.NodeID == prefs.Persist().NodeID())
|
||||
func (pm *profileManager) findMatchingProfiles(prefs ipn.PrefsView) []ipn.LoginProfileView {
|
||||
return pm.matchingProfiles(func(p ipn.LoginProfileView) bool {
|
||||
return p.ControlURL() == prefs.ControlURL() &&
|
||||
(p.UserProfile().ID == prefs.Persist().UserProfile().ID ||
|
||||
p.NodeID() == prefs.Persist().NodeID())
|
||||
})
|
||||
}
|
||||
|
||||
@ -157,18 +157,18 @@ func (pm *profileManager) findMatchingProfiles(prefs ipn.PrefsView) []*ipn.Login
|
||||
// accessible to the current user.
|
||||
func (pm *profileManager) ProfileIDForName(name string) ipn.ProfileID {
|
||||
p := pm.findProfileByName(name)
|
||||
if p == nil {
|
||||
if !p.Valid() {
|
||||
return ""
|
||||
}
|
||||
return p.ID
|
||||
return p.ID()
|
||||
}
|
||||
|
||||
func (pm *profileManager) findProfileByName(name string) *ipn.LoginProfile {
|
||||
out := pm.matchingProfiles(func(p *ipn.LoginProfile) bool {
|
||||
return p.Name == name
|
||||
func (pm *profileManager) findProfileByName(name string) ipn.LoginProfileView {
|
||||
out := pm.matchingProfiles(func(p ipn.LoginProfileView) bool {
|
||||
return p.Name() == name
|
||||
})
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
return ipn.LoginProfileView{}
|
||||
}
|
||||
if len(out) > 1 {
|
||||
pm.logf("[unexpected] multiple profiles with the same name")
|
||||
@ -176,12 +176,12 @@ func (pm *profileManager) findProfileByName(name string) *ipn.LoginProfile {
|
||||
return out[0]
|
||||
}
|
||||
|
||||
func (pm *profileManager) findProfileByKey(key ipn.StateKey) *ipn.LoginProfile {
|
||||
out := pm.matchingProfiles(func(p *ipn.LoginProfile) bool {
|
||||
return p.Key == key
|
||||
func (pm *profileManager) findProfileByKey(key ipn.StateKey) ipn.LoginProfileView {
|
||||
out := pm.matchingProfiles(func(p ipn.LoginProfileView) bool {
|
||||
return p.Key() == key
|
||||
})
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
return ipn.LoginProfileView{}
|
||||
}
|
||||
if len(out) > 1 {
|
||||
pm.logf("[unexpected] multiple profiles with the same key")
|
||||
@ -194,8 +194,8 @@ func (pm *profileManager) setUnattendedModeAsConfigured() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if pm.currentProfile.Key != "" && pm.prefs.ForceDaemon() {
|
||||
return pm.WriteState(ipn.ServerModeStartKey, []byte(pm.currentProfile.Key))
|
||||
if pm.currentProfile.Key() != "" && pm.prefs.ForceDaemon() {
|
||||
return pm.WriteState(ipn.ServerModeStartKey, []byte(pm.currentProfile.Key()))
|
||||
} else {
|
||||
return pm.WriteState(ipn.ServerModeStartKey, nil)
|
||||
}
|
||||
@ -229,29 +229,36 @@ func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView, np ipn.NetworkProfile)
|
||||
existing = existing[1:]
|
||||
for _, p := range existing {
|
||||
// Clear the state.
|
||||
if err := pm.store.WriteState(p.Key, nil); err != nil {
|
||||
if err := pm.store.WriteState(p.Key(), nil); err != nil {
|
||||
// We couldn't delete the state, so keep the profile around.
|
||||
continue
|
||||
}
|
||||
// Remove the profile, knownProfiles will be persisted
|
||||
// in [profileManager.setProfilePrefs] below.
|
||||
delete(pm.knownProfiles, p.ID)
|
||||
delete(pm.knownProfiles, p.ID())
|
||||
}
|
||||
}
|
||||
pm.currentProfile = cp
|
||||
if err := pm.SetProfilePrefs(cp, prefsIn, np); err != nil {
|
||||
cp, err := pm.setProfilePrefs(nil, prefsIn, np)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pm.setProfileAsUserDefault(cp)
|
||||
|
||||
}
|
||||
|
||||
// SetProfilePrefs is like [profileManager.SetPrefs], but sets prefs for the specified [ipn.LoginProfile]
|
||||
// which is not necessarily the [profileManager.CurrentProfile]. It returns an [errProfileAccessDenied]
|
||||
// if the specified profile is not accessible by the current user.
|
||||
func (pm *profileManager) SetProfilePrefs(lp *ipn.LoginProfile, prefsIn ipn.PrefsView, np ipn.NetworkProfile) error {
|
||||
if err := pm.checkProfileAccess(lp); err != nil {
|
||||
return err
|
||||
// setProfilePrefs is like [profileManager.SetPrefs], but sets prefs for the specified [ipn.LoginProfile],
|
||||
// returning a read-only view of the updated profile on success. If the specified profile is nil,
|
||||
// it defaults to the current profile. If the profile is not accessible by the current user,
|
||||
// the method returns an [errProfileAccessDenied].
|
||||
func (pm *profileManager) setProfilePrefs(lp *ipn.LoginProfile, prefsIn ipn.PrefsView, np ipn.NetworkProfile) (ipn.LoginProfileView, error) {
|
||||
isCurrentProfile := lp == nil || (lp.ID != "" && lp.ID == pm.currentProfile.ID())
|
||||
if isCurrentProfile {
|
||||
lp = pm.CurrentProfile().AsStruct()
|
||||
}
|
||||
|
||||
if err := pm.checkProfileAccess(lp.View()); err != nil {
|
||||
return ipn.LoginProfileView{}, err
|
||||
}
|
||||
|
||||
// An empty profile.ID indicates that the profile is new, the node info wasn't available,
|
||||
@ -291,23 +298,29 @@ func (pm *profileManager) SetProfilePrefs(lp *ipn.LoginProfile, prefsIn ipn.Pref
|
||||
lp.UserProfile = up
|
||||
lp.NetworkProfile = np
|
||||
|
||||
// Update the current profile view to reflect the changes
|
||||
// if the specified profile is the current profile.
|
||||
if isCurrentProfile {
|
||||
pm.currentProfile = lp.View()
|
||||
}
|
||||
|
||||
// An empty profile.ID indicates that the node info is not available yet,
|
||||
// and the profile doesn't need to be saved on disk.
|
||||
if lp.ID != "" {
|
||||
pm.knownProfiles[lp.ID] = lp
|
||||
pm.knownProfiles[lp.ID] = lp.View()
|
||||
if err := pm.writeKnownProfiles(); err != nil {
|
||||
return err
|
||||
return ipn.LoginProfileView{}, err
|
||||
}
|
||||
// Clone prefsIn and create a read-only view as a safety measure to
|
||||
// prevent accidental preference mutations, both externally and internally.
|
||||
if err := pm.setProfilePrefsNoPermCheck(lp, prefsIn.AsStruct().View()); err != nil {
|
||||
return err
|
||||
if err := pm.setProfilePrefsNoPermCheck(lp.View(), prefsIn.AsStruct().View()); err != nil {
|
||||
return ipn.LoginProfileView{}, err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return lp.View(), nil
|
||||
}
|
||||
|
||||
func newUnusedID(knownProfiles map[ipn.ProfileID]*ipn.LoginProfile) (ipn.ProfileID, ipn.StateKey) {
|
||||
func newUnusedID(knownProfiles map[ipn.ProfileID]ipn.LoginProfileView) (ipn.ProfileID, ipn.StateKey) {
|
||||
var idb [2]byte
|
||||
for {
|
||||
rand.Read(idb[:])
|
||||
@ -326,14 +339,14 @@ func newUnusedID(knownProfiles map[ipn.ProfileID]*ipn.LoginProfile) (ipn.Profile
|
||||
// The method does not perform any additional checks on the specified
|
||||
// profile, such as verifying the caller's access rights or checking
|
||||
// if another profile for the same node already exists.
|
||||
func (pm *profileManager) setProfilePrefsNoPermCheck(profile *ipn.LoginProfile, clonedPrefs ipn.PrefsView) error {
|
||||
func (pm *profileManager) setProfilePrefsNoPermCheck(profile ipn.LoginProfileView, clonedPrefs ipn.PrefsView) error {
|
||||
isCurrentProfile := pm.currentProfile == profile
|
||||
if isCurrentProfile {
|
||||
pm.prefs = clonedPrefs
|
||||
pm.updateHealth()
|
||||
}
|
||||
if profile.Key != "" {
|
||||
if err := pm.writePrefsToStore(profile.Key, clonedPrefs); err != nil {
|
||||
if profile.Key() != "" {
|
||||
if err := pm.writePrefsToStore(profile.Key(), clonedPrefs); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !isCurrentProfile {
|
||||
@ -362,11 +375,11 @@ func (pm *profileManager) writePrefsToStore(key ipn.StateKey, prefs ipn.PrefsVie
|
||||
}
|
||||
|
||||
// Profiles returns the list of known profiles accessible to the current user.
|
||||
func (pm *profileManager) Profiles() []ipn.LoginProfile {
|
||||
func (pm *profileManager) Profiles() []ipn.LoginProfileView {
|
||||
allProfiles := pm.allProfiles()
|
||||
out := make([]ipn.LoginProfile, len(allProfiles))
|
||||
out := make([]ipn.LoginProfileView, len(allProfiles))
|
||||
for i, p := range allProfiles {
|
||||
out[i] = *p
|
||||
out[i] = p
|
||||
}
|
||||
return out
|
||||
}
|
||||
@ -374,26 +387,26 @@ func (pm *profileManager) Profiles() []ipn.LoginProfile {
|
||||
// ProfileByID returns a profile with the given id, if it is accessible to the current user.
|
||||
// If the profile exists but is not accessible to the current user, it returns an [errProfileAccessDenied].
|
||||
// If the profile does not exist, it returns an [errProfileNotFound].
|
||||
func (pm *profileManager) ProfileByID(id ipn.ProfileID) (ipn.LoginProfile, error) {
|
||||
func (pm *profileManager) ProfileByID(id ipn.ProfileID) (ipn.LoginProfileView, error) {
|
||||
kp, err := pm.profileByIDNoPermCheck(id)
|
||||
if err != nil {
|
||||
return ipn.LoginProfile{}, err
|
||||
return ipn.LoginProfileView{}, err
|
||||
}
|
||||
if err := pm.checkProfileAccess(kp); err != nil {
|
||||
return ipn.LoginProfile{}, err
|
||||
return ipn.LoginProfileView{}, err
|
||||
}
|
||||
return *kp, nil
|
||||
return kp, nil
|
||||
}
|
||||
|
||||
// profileByIDNoPermCheck is like [profileManager.ProfileByID], but it doesn't
|
||||
// check user's access rights to the profile.
|
||||
func (pm *profileManager) profileByIDNoPermCheck(id ipn.ProfileID) (*ipn.LoginProfile, error) {
|
||||
if id == pm.currentProfile.ID {
|
||||
func (pm *profileManager) profileByIDNoPermCheck(id ipn.ProfileID) (ipn.LoginProfileView, error) {
|
||||
if id == pm.currentProfile.ID() {
|
||||
return pm.currentProfile, nil
|
||||
}
|
||||
kp, ok := pm.knownProfiles[id]
|
||||
if !ok {
|
||||
return nil, errProfileNotFound
|
||||
return ipn.LoginProfileView{}, errProfileNotFound
|
||||
}
|
||||
return kp, nil
|
||||
}
|
||||
@ -412,11 +425,11 @@ func (pm *profileManager) ProfilePrefs(id ipn.ProfileID) (ipn.PrefsView, error)
|
||||
return pm.profilePrefs(kp)
|
||||
}
|
||||
|
||||
func (pm *profileManager) profilePrefs(p *ipn.LoginProfile) (ipn.PrefsView, error) {
|
||||
if p.ID == pm.currentProfile.ID {
|
||||
func (pm *profileManager) profilePrefs(p ipn.LoginProfileView) (ipn.PrefsView, error) {
|
||||
if p.ID() == pm.currentProfile.ID() {
|
||||
return pm.prefs, nil
|
||||
}
|
||||
return pm.loadSavedPrefs(p.Key)
|
||||
return pm.loadSavedPrefs(p.Key())
|
||||
}
|
||||
|
||||
// SwitchProfile switches to the profile with the given id.
|
||||
@ -429,14 +442,14 @@ func (pm *profileManager) SwitchProfile(id ipn.ProfileID) error {
|
||||
if !ok {
|
||||
return errProfileNotFound
|
||||
}
|
||||
if pm.currentProfile != nil && kp.ID == pm.currentProfile.ID && pm.prefs.Valid() {
|
||||
if pm.currentProfile.Valid() && kp.ID() == pm.currentProfile.ID() && pm.prefs.Valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := pm.checkProfileAccess(kp); err != nil {
|
||||
return fmt.Errorf("%w: profile %q is not accessible to the current user", err, id)
|
||||
}
|
||||
prefs, err := pm.loadSavedPrefs(kp.Key)
|
||||
prefs, err := pm.loadSavedPrefs(kp.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -459,8 +472,8 @@ func (pm *profileManager) SwitchToDefaultProfile() error {
|
||||
|
||||
// setProfileAsUserDefault sets the specified profile as the default for the current user.
|
||||
// It returns an [errProfileAccessDenied] if the specified profile is not accessible to the current user.
|
||||
func (pm *profileManager) setProfileAsUserDefault(profile *ipn.LoginProfile) error {
|
||||
if profile.Key == "" {
|
||||
func (pm *profileManager) setProfileAsUserDefault(profile ipn.LoginProfileView) error {
|
||||
if profile.Key() == "" {
|
||||
// The profile has not been persisted yet; ignore it for now.
|
||||
return nil
|
||||
}
|
||||
@ -468,7 +481,7 @@ func (pm *profileManager) setProfileAsUserDefault(profile *ipn.LoginProfile) err
|
||||
return errProfileAccessDenied
|
||||
}
|
||||
k := ipn.CurrentProfileKey(string(pm.currentUserID))
|
||||
return pm.WriteState(k, []byte(profile.Key))
|
||||
return pm.WriteState(k, []byte(profile.Key()))
|
||||
}
|
||||
|
||||
func (pm *profileManager) loadSavedPrefs(key ipn.StateKey) (ipn.PrefsView, error) {
|
||||
@ -507,10 +520,10 @@ func (pm *profileManager) loadSavedPrefs(key ipn.StateKey) (ipn.PrefsView, error
|
||||
return savedPrefs.View(), nil
|
||||
}
|
||||
|
||||
// CurrentProfile returns the current LoginProfile.
|
||||
// CurrentProfile returns a read-only [ipn.LoginProfileView] of the current profile.
|
||||
// The value may be zero if the profile is not persisted.
|
||||
func (pm *profileManager) CurrentProfile() ipn.LoginProfile {
|
||||
return *pm.currentProfile
|
||||
func (pm *profileManager) CurrentProfile() ipn.LoginProfileView {
|
||||
return pm.currentProfile
|
||||
}
|
||||
|
||||
// errProfileNotFound is returned by methods that accept a ProfileID
|
||||
@ -533,7 +546,7 @@ var errProfileAccessDenied = errors.New("profile access denied")
|
||||
// recommended to call [profileManager.SwitchProfile] first.
|
||||
func (pm *profileManager) DeleteProfile(id ipn.ProfileID) error {
|
||||
metricDeleteProfile.Add(1)
|
||||
if id == pm.currentProfile.ID {
|
||||
if id == pm.currentProfile.ID() {
|
||||
return pm.deleteCurrentProfile()
|
||||
}
|
||||
kp, ok := pm.knownProfiles[id]
|
||||
@ -550,7 +563,7 @@ func (pm *profileManager) deleteCurrentProfile() error {
|
||||
if err := pm.checkProfileAccess(pm.currentProfile); err != nil {
|
||||
return err
|
||||
}
|
||||
if pm.currentProfile.ID == "" {
|
||||
if pm.currentProfile.ID() == "" {
|
||||
// Deleting the in-memory only new profile, just create a new one.
|
||||
pm.NewProfile()
|
||||
return nil
|
||||
@ -560,14 +573,14 @@ func (pm *profileManager) deleteCurrentProfile() error {
|
||||
|
||||
// deleteProfileNoPermCheck is like [profileManager.DeleteProfile],
|
||||
// but it doesn't check user's access rights to the profile.
|
||||
func (pm *profileManager) deleteProfileNoPermCheck(profile *ipn.LoginProfile) error {
|
||||
if profile.ID == pm.currentProfile.ID {
|
||||
func (pm *profileManager) deleteProfileNoPermCheck(profile ipn.LoginProfileView) error {
|
||||
if profile.ID() == pm.currentProfile.ID() {
|
||||
pm.NewProfile()
|
||||
}
|
||||
if err := pm.WriteState(profile.Key, nil); err != nil {
|
||||
if err := pm.WriteState(profile.Key(), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(pm.knownProfiles, profile.ID)
|
||||
delete(pm.knownProfiles, profile.ID())
|
||||
return pm.writeKnownProfiles()
|
||||
}
|
||||
|
||||
@ -578,7 +591,7 @@ func (pm *profileManager) DeleteAllProfilesForUser() error {
|
||||
|
||||
currentProfileDeleted := false
|
||||
writeKnownProfiles := func() error {
|
||||
if currentProfileDeleted || pm.currentProfile.ID == "" {
|
||||
if currentProfileDeleted || pm.currentProfile.ID() == "" {
|
||||
pm.NewProfile()
|
||||
}
|
||||
return pm.writeKnownProfiles()
|
||||
@ -589,14 +602,14 @@ func (pm *profileManager) DeleteAllProfilesForUser() error {
|
||||
// Skip profiles we don't have access to.
|
||||
continue
|
||||
}
|
||||
if err := pm.WriteState(kp.Key, nil); err != nil {
|
||||
if err := pm.WriteState(kp.Key(), nil); err != nil {
|
||||
// Write to remove references to profiles we've already deleted, but
|
||||
// return the original error.
|
||||
writeKnownProfiles()
|
||||
return err
|
||||
}
|
||||
delete(pm.knownProfiles, kp.ID)
|
||||
if kp.ID == pm.currentProfile.ID {
|
||||
delete(pm.knownProfiles, kp.ID())
|
||||
if kp.ID() == pm.currentProfile.ID() {
|
||||
currentProfileDeleted = true
|
||||
}
|
||||
}
|
||||
@ -633,26 +646,27 @@ func (pm *profileManager) NewProfileForUser(uid ipn.WindowsUserID) {
|
||||
|
||||
pm.prefs = defaultPrefs
|
||||
pm.updateHealth()
|
||||
pm.currentProfile = &ipn.LoginProfile{LocalUserID: uid}
|
||||
newProfile := &ipn.LoginProfile{LocalUserID: uid}
|
||||
pm.currentProfile = newProfile.View()
|
||||
}
|
||||
|
||||
// newProfileWithPrefs creates a new profile with the specified prefs and assigns
|
||||
// the specified uid as the profile owner. If switchNow is true, it switches to the
|
||||
// newly created profile immediately. It returns the newly created profile on success,
|
||||
// or an error on failure.
|
||||
func (pm *profileManager) newProfileWithPrefs(uid ipn.WindowsUserID, prefs ipn.PrefsView, switchNow bool) (*ipn.LoginProfile, error) {
|
||||
func (pm *profileManager) newProfileWithPrefs(uid ipn.WindowsUserID, prefs ipn.PrefsView, switchNow bool) (ipn.LoginProfileView, error) {
|
||||
metricNewProfile.Add(1)
|
||||
|
||||
profile := &ipn.LoginProfile{LocalUserID: uid}
|
||||
if err := pm.SetProfilePrefs(profile, prefs, ipn.NetworkProfile{}); err != nil {
|
||||
return nil, err
|
||||
profile, err := pm.setProfilePrefs(&ipn.LoginProfile{LocalUserID: uid}, prefs, ipn.NetworkProfile{})
|
||||
if err != nil {
|
||||
return ipn.LoginProfileView{}, err
|
||||
}
|
||||
if switchNow {
|
||||
pm.currentProfile = profile
|
||||
pm.prefs = prefs.AsStruct().View()
|
||||
pm.updateHealth()
|
||||
if err := pm.setProfileAsUserDefault(profile); err != nil {
|
||||
return nil, err
|
||||
return ipn.LoginProfileView{}, err
|
||||
}
|
||||
}
|
||||
return profile, nil
|
||||
@ -711,8 +725,8 @@ func readAutoStartKey(store ipn.StateStore, goos string) (ipn.StateKey, error) {
|
||||
return ipn.StateKey(autoStartKey), nil
|
||||
}
|
||||
|
||||
func readKnownProfiles(store ipn.StateStore) (map[ipn.ProfileID]*ipn.LoginProfile, error) {
|
||||
var knownProfiles map[ipn.ProfileID]*ipn.LoginProfile
|
||||
func readKnownProfiles(store ipn.StateStore) (map[ipn.ProfileID]ipn.LoginProfileView, error) {
|
||||
var knownProfiles map[ipn.ProfileID]ipn.LoginProfileView
|
||||
prfB, err := store.ReadState(ipn.KnownProfilesStateKey)
|
||||
switch err {
|
||||
case nil:
|
||||
@ -720,7 +734,7 @@ func readKnownProfiles(store ipn.StateStore) (map[ipn.ProfileID]*ipn.LoginProfil
|
||||
return nil, fmt.Errorf("unmarshaling known profiles: %w", err)
|
||||
}
|
||||
case ipn.ErrStateNotExist:
|
||||
knownProfiles = make(map[ipn.ProfileID]*ipn.LoginProfile)
|
||||
knownProfiles = make(map[ipn.ProfileID]ipn.LoginProfileView)
|
||||
default:
|
||||
return nil, fmt.Errorf("calling ReadState on state store: %w", err)
|
||||
}
|
||||
@ -749,17 +763,17 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, ht *healt
|
||||
|
||||
if stateKey != "" {
|
||||
for _, v := range knownProfiles {
|
||||
if v.Key == stateKey {
|
||||
if v.Key() == stateKey {
|
||||
pm.currentProfile = v
|
||||
}
|
||||
}
|
||||
if pm.currentProfile == nil {
|
||||
if !pm.currentProfile.Valid() {
|
||||
if suf, ok := strings.CutPrefix(string(stateKey), "user-"); ok {
|
||||
pm.currentUserID = ipn.WindowsUserID(suf)
|
||||
}
|
||||
pm.NewProfile()
|
||||
} else {
|
||||
pm.currentUserID = pm.currentProfile.LocalUserID
|
||||
pm.currentUserID = pm.currentProfile.LocalUserID()
|
||||
}
|
||||
prefs, err := pm.loadSavedPrefs(stateKey)
|
||||
if err != nil {
|
||||
@ -788,18 +802,18 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, ht *healt
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func (pm *profileManager) migrateFromLegacyPrefs(uid ipn.WindowsUserID, switchNow bool) (*ipn.LoginProfile, error) {
|
||||
func (pm *profileManager) migrateFromLegacyPrefs(uid ipn.WindowsUserID, switchNow bool) (ipn.LoginProfileView, error) {
|
||||
metricMigration.Add(1)
|
||||
sentinel, prefs, err := pm.loadLegacyPrefs(uid)
|
||||
if err != nil {
|
||||
metricMigrationError.Add(1)
|
||||
return nil, fmt.Errorf("load legacy prefs: %w", err)
|
||||
return ipn.LoginProfileView{}, fmt.Errorf("load legacy prefs: %w", err)
|
||||
}
|
||||
pm.dlogf("loaded legacy preferences; sentinel=%q", sentinel)
|
||||
profile, err := pm.newProfileWithPrefs(uid, prefs, switchNow)
|
||||
if err != nil {
|
||||
metricMigrationError.Add(1)
|
||||
return nil, fmt.Errorf("migrating _daemon profile: %w", err)
|
||||
return ipn.LoginProfileView{}, fmt.Errorf("migrating _daemon profile: %w", err)
|
||||
}
|
||||
pm.completeMigration(sentinel)
|
||||
pm.dlogf("completed legacy preferences migration with sentinel=%q", sentinel)
|
||||
@ -809,8 +823,8 @@ func (pm *profileManager) migrateFromLegacyPrefs(uid ipn.WindowsUserID, switchNo
|
||||
|
||||
func (pm *profileManager) requiresBackfill() bool {
|
||||
return pm != nil &&
|
||||
pm.currentProfile != nil &&
|
||||
pm.currentProfile.NetworkProfile.RequiresBackfill()
|
||||
pm.currentProfile.Valid() &&
|
||||
pm.currentProfile.NetworkProfile().RequiresBackfill()
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -52,11 +52,11 @@ func TestProfileCurrentUserSwitch(t *testing.T) {
|
||||
pm.SetCurrentUserID("user1")
|
||||
newProfile(t, "user1")
|
||||
cp := pm.currentProfile
|
||||
pm.DeleteProfile(cp.ID)
|
||||
if pm.currentProfile == nil {
|
||||
pm.DeleteProfile(cp.ID())
|
||||
if !pm.currentProfile.Valid() {
|
||||
t.Fatal("currentProfile is nil")
|
||||
} else if pm.currentProfile.ID != "" {
|
||||
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID)
|
||||
} else if pm.currentProfile.ID() != "" {
|
||||
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID())
|
||||
}
|
||||
if !pm.CurrentPrefs().Equals(defaultPrefs) {
|
||||
t.Fatalf("CurrentPrefs() = %v, want emptyPrefs", pm.CurrentPrefs().Pretty())
|
||||
@ -67,10 +67,10 @@ func TestProfileCurrentUserSwitch(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pm.SetCurrentUserID("user1")
|
||||
if pm.currentProfile == nil {
|
||||
if !pm.currentProfile.Valid() {
|
||||
t.Fatal("currentProfile is nil")
|
||||
} else if pm.currentProfile.ID != "" {
|
||||
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID)
|
||||
} else if pm.currentProfile.ID() != "" {
|
||||
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID())
|
||||
}
|
||||
if !pm.CurrentPrefs().Equals(defaultPrefs) {
|
||||
t.Fatalf("CurrentPrefs() = %v, want emptyPrefs", pm.CurrentPrefs().Pretty())
|
||||
@ -110,8 +110,8 @@ func TestProfileList(t *testing.T) {
|
||||
t.Fatalf("got %d profiles, want %d", len(got), len(want))
|
||||
}
|
||||
for i, w := range want {
|
||||
if got[i].Name != w {
|
||||
t.Errorf("got profile %d name %q, want %q", i, got[i].Name, w)
|
||||
if got[i].Name() != w {
|
||||
t.Errorf("got profile %d name %q, want %q", i, got[i].Name(), w)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,10 +129,10 @@ func TestProfileList(t *testing.T) {
|
||||
|
||||
pm.SetCurrentUserID("user1")
|
||||
checkProfiles(t, "alice", "bob")
|
||||
if lp := pm.findProfileByKey(carol.Key); lp != nil {
|
||||
if lp := pm.findProfileByKey(carol.Key()); lp.Valid() {
|
||||
t.Fatalf("found profile for user2 in user1's profile list")
|
||||
}
|
||||
if lp := pm.findProfileByName(carol.Name); lp != nil {
|
||||
if lp := pm.findProfileByName(carol.Name()); lp.Valid() {
|
||||
t.Fatalf("found profile for user2 in user1's profile list")
|
||||
}
|
||||
|
||||
@ -294,7 +294,7 @@ func TestProfileDupe(t *testing.T) {
|
||||
profs := pm.Profiles()
|
||||
var got []*persist.Persist
|
||||
for _, p := range profs {
|
||||
prefs, err := pm.loadSavedPrefs(p.Key)
|
||||
prefs, err := pm.loadSavedPrefs(p.Key())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -328,9 +328,9 @@ func TestProfileManagement(t *testing.T) {
|
||||
checkProfiles := func(t *testing.T) {
|
||||
t.Helper()
|
||||
prof := pm.CurrentProfile()
|
||||
t.Logf("\tCurrentProfile = %q", prof)
|
||||
if prof.Name != wantCurProfile {
|
||||
t.Fatalf("CurrentProfile = %q; want %q", prof, wantCurProfile)
|
||||
t.Logf("\tCurrentProfile = %q", prof.Name())
|
||||
if prof.Name() != wantCurProfile {
|
||||
t.Fatalf("CurrentProfile = %q; want %q", prof.Name(), wantCurProfile)
|
||||
}
|
||||
profiles := pm.Profiles()
|
||||
wantLen := len(wantProfiles)
|
||||
@ -349,13 +349,13 @@ func TestProfileManagement(t *testing.T) {
|
||||
t.Fatalf("CurrentPrefs = %v; want %v", p.Pretty(), wantProfiles[wantCurProfile].Pretty())
|
||||
}
|
||||
for _, p := range profiles {
|
||||
got, err := pm.loadSavedPrefs(p.Key)
|
||||
got, err := pm.loadSavedPrefs(p.Key())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Use Hostname as a proxy for all prefs.
|
||||
if !got.Equals(wantProfiles[p.Name]) {
|
||||
t.Fatalf("Prefs for profile %q =\n got=%+v\nwant=%v", p, got.Pretty(), wantProfiles[p.Name].Pretty())
|
||||
if !got.Equals(wantProfiles[p.Name()]) {
|
||||
t.Fatalf("Prefs for profile %q =\n got=%+v\nwant=%v", p.Name(), got.Pretty(), wantProfiles[p.Name()].Pretty())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -422,7 +422,7 @@ func TestProfileManagement(t *testing.T) {
|
||||
checkProfiles(t)
|
||||
|
||||
t.Logf("Delete default profile")
|
||||
if err := pm.DeleteProfile(pm.findProfileByName("user@1.example.com").ID); err != nil {
|
||||
if err := pm.DeleteProfile(pm.ProfileIDForName("user@1.example.com")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
delete(wantProfiles, "user@1.example.com")
|
||||
@ -506,9 +506,9 @@ func TestProfileManagementWindows(t *testing.T) {
|
||||
checkProfiles := func(t *testing.T) {
|
||||
t.Helper()
|
||||
prof := pm.CurrentProfile()
|
||||
t.Logf("\tCurrentProfile = %q", prof)
|
||||
if prof.Name != wantCurProfile {
|
||||
t.Fatalf("CurrentProfile = %q; want %q", prof, wantCurProfile)
|
||||
t.Logf("\tCurrentProfile = %q", prof.Name())
|
||||
if prof.Name() != wantCurProfile {
|
||||
t.Fatalf("CurrentProfile = %q; want %q", prof.Name(), wantCurProfile)
|
||||
}
|
||||
if p := pm.CurrentPrefs(); !p.Equals(wantProfiles[wantCurProfile]) {
|
||||
t.Fatalf("CurrentPrefs = %+v; want %+v", p.Pretty(), wantProfiles[wantCurProfile].Pretty())
|
||||
|
@ -318,7 +318,7 @@ func (b *LocalBackend) setServeConfigLocked(config *ipn.ServeConfig, etag string
|
||||
bs = j
|
||||
}
|
||||
|
||||
profileID := b.pm.CurrentProfile().ID
|
||||
profileID := b.pm.CurrentProfile().ID()
|
||||
confKey := ipn.ServeConfigKey(profileID)
|
||||
if err := b.store.WriteState(confKey, bs); err != nil {
|
||||
return fmt.Errorf("writing ServeConfig to StateStore: %w", err)
|
||||
|
@ -898,7 +898,7 @@ func newTestBackend(t *testing.T) *LocalBackend {
|
||||
b.SetVarRoot(dir)
|
||||
|
||||
pm := must.Get(newProfileManager(new(mem.Store), logf, new(health.Tracker)))
|
||||
pm.currentProfile = &ipn.LoginProfile{ID: "id0"}
|
||||
pm.currentProfile = (&ipn.LoginProfile{ID: "id0"}).View()
|
||||
b.pm = pm
|
||||
|
||||
b.netMap = &netmap.NetworkMap{
|
||||
|
@ -2601,8 +2601,8 @@ func (h *Handler) serveProfiles(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case httpm.GET:
|
||||
profiles := h.b.ListProfiles()
|
||||
profileIndex := slices.IndexFunc(profiles, func(p ipn.LoginProfile) bool {
|
||||
return p.ID == profileID
|
||||
profileIndex := slices.IndexFunc(profiles, func(p ipn.LoginProfileView) bool {
|
||||
return p.ID() == profileID
|
||||
})
|
||||
if profileIndex == -1 {
|
||||
http.Error(w, "Profile not found", http.StatusNotFound)
|
||||
|
Loading…
x
Reference in New Issue
Block a user