mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-07 08:07:42 +00:00
ipn/ipnlocal: delete profile on logout
Updates #713 Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
0544d6ed04
commit
26d1fc867e
@ -799,19 +799,22 @@ func (b *LocalBackend) setClientStatus(st controlclient.Status) {
|
|||||||
b.send(ipn.Notify{LoginFinished: &empty.Message{}})
|
b.send(ipn.Notify{LoginFinished: &empty.Message{}})
|
||||||
}
|
}
|
||||||
|
|
||||||
prefsChanged := false
|
|
||||||
|
|
||||||
// Lock b once and do only the things that require locking.
|
// Lock b once and do only the things that require locking.
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|
||||||
if st.LogoutFinished != nil {
|
if st.LogoutFinished != nil {
|
||||||
// Since we're logged out now, our netmap cache is invalid.
|
if p := b.pm.CurrentPrefs(); p.Persist() == nil || p.Persist().LoginName == "" {
|
||||||
// Since st.NetMap==nil means "netmap is unchanged", there is
|
b.mu.Unlock()
|
||||||
// no other way to represent this change.
|
return
|
||||||
b.setNetMapLocked(nil)
|
}
|
||||||
b.e.SetNetworkMap(new(netmap.NetworkMap))
|
if err := b.pm.DeleteProfile(b.pm.CurrentProfile().ID); err != nil {
|
||||||
|
b.logf("error deleting profile: %v", err)
|
||||||
|
}
|
||||||
|
b.resetForProfileChangeLockedOnEntry()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefsChanged := false
|
||||||
prefs := b.pm.CurrentPrefs().AsStruct()
|
prefs := b.pm.CurrentPrefs().AsStruct()
|
||||||
netMap := b.netMap
|
netMap := b.netMap
|
||||||
interact := b.interact
|
interact := b.interact
|
||||||
|
@ -268,6 +268,11 @@ func (pm *profileManager) CurrentProfile() ipn.LoginProfile {
|
|||||||
// useful for deleting the last profile. In other cases, it is
|
// useful for deleting the last profile. In other cases, it is
|
||||||
// recommended to call SwitchProfile() first.
|
// recommended to call SwitchProfile() first.
|
||||||
func (pm *profileManager) DeleteProfile(id ipn.ProfileID) error {
|
func (pm *profileManager) DeleteProfile(id ipn.ProfileID) error {
|
||||||
|
if id == "" && pm.isNewProfile {
|
||||||
|
// Deleting the in-memory only new profile, just create a new one.
|
||||||
|
pm.NewProfile()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
kp, ok := pm.knownProfiles[id]
|
kp, ok := pm.knownProfiles[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errProfileNotFound
|
return errProfileNotFound
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
|
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
|
"tailscale.com/envknob"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/store/mem"
|
"tailscale.com/ipn/store/mem"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
@ -81,6 +82,7 @@ func (nt *notifyThrottler) drain(count int) []ipn.Notify {
|
|||||||
// no more notifications expected
|
// no more notifications expected
|
||||||
close(ch)
|
close(ch)
|
||||||
|
|
||||||
|
nt.t.Log(nn)
|
||||||
return nn
|
return nn
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +127,9 @@ func (cc *mockControl) populateKeys() (newKeys bool) {
|
|||||||
newKeys = true
|
newKeys = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cc.persist == nil {
|
||||||
|
cc.persist = &persist.Persist{}
|
||||||
|
}
|
||||||
if cc.persist != nil && cc.persist.PrivateNodeKey.IsZero() {
|
if cc.persist != nil && cc.persist.PrivateNodeKey.IsZero() {
|
||||||
cc.logf("Generating a new nodekey.")
|
cc.logf("Generating a new nodekey.")
|
||||||
cc.persist.OldPrivateNodeKey = cc.persist.PrivateNodeKey
|
cc.persist.OldPrivateNodeKey = cc.persist.PrivateNodeKey
|
||||||
@ -281,6 +286,8 @@ func (cc *mockControl) UpdateEndpoints(endpoints []tailcfg.Endpoint) {
|
|||||||
// predictable, but maybe a bit less thorough. This is more of an overall
|
// predictable, but maybe a bit less thorough. This is more of an overall
|
||||||
// state machine test than a test of the wgengine+magicsock integration.
|
// state machine test than a test of the wgengine+magicsock integration.
|
||||||
func TestStateMachine(t *testing.T) {
|
func TestStateMachine(t *testing.T) {
|
||||||
|
envknob.Setenv("TAILSCALE_USE_WIP_CODE", "1")
|
||||||
|
defer envknob.Setenv("TAILSCALE_USE_WIP_CODE", "")
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
logf := tstest.WhileTestRunningLogger(t)
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
@ -304,10 +311,10 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.opts = opts
|
cc.opts = opts
|
||||||
cc.logfActual = opts.Logf
|
cc.logfActual = opts.Logf
|
||||||
cc.authBlocked = true
|
cc.authBlocked = true
|
||||||
cc.persist = &cc.opts.Persist
|
cc.persist = cc.opts.Persist.Clone()
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
|
|
||||||
cc.logf("ccGen: new mockControl.")
|
t.Logf("ccGen: new mockControl.")
|
||||||
cc.called("New")
|
cc.called("New")
|
||||||
return cc, nil
|
return cc, nil
|
||||||
})
|
})
|
||||||
@ -585,25 +592,29 @@ func TestStateMachine(t *testing.T) {
|
|||||||
|
|
||||||
// Let's make the logout succeed.
|
// Let's make the logout succeed.
|
||||||
t.Logf("\n\nLogout (async) - succeed")
|
t.Logf("\n\nLogout (async) - succeed")
|
||||||
notifies.expect(1)
|
notifies.expect(3)
|
||||||
cc.setAuthBlocked(true)
|
cc.setAuthBlocked(true)
|
||||||
cc.send(nil, "", false, nil)
|
cc.send(nil, "", false, nil)
|
||||||
{
|
{
|
||||||
nn := notifies.drain(1)
|
nn := notifies.drain(3)
|
||||||
cc.assertCalls("unpause", "unpause")
|
cc.assertCalls("unpause", "unpause", "Shutdown", "unpause", "New", "unpause")
|
||||||
c.Assert(nn[0].State, qt.IsNotNil)
|
c.Assert(nn[0].State, qt.IsNotNil)
|
||||||
c.Assert(ipn.NeedsLogin, qt.Equals, *nn[0].State)
|
c.Assert(*nn[0].State, qt.Equals, ipn.NoState)
|
||||||
c.Assert(b.Prefs().LoggedOut(), qt.IsTrue)
|
c.Assert(nn[1].Prefs, qt.IsNotNil) // emptyPrefs
|
||||||
|
c.Assert(nn[2].State, qt.IsNotNil)
|
||||||
|
c.Assert(*nn[2].State, qt.Equals, ipn.NeedsLogin)
|
||||||
|
c.Assert(b.Prefs().LoggedOut(), qt.IsFalse)
|
||||||
c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
|
c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
|
||||||
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
c.Assert(b.State(), qt.Equals, ipn.NeedsLogin)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A second logout should do nothing, since the prefs haven't changed.
|
// A second logout should reset all prefs.
|
||||||
t.Logf("\n\nLogout2 (async)")
|
t.Logf("\n\nLogout2 (async)")
|
||||||
notifies.expect(0)
|
notifies.expect(1)
|
||||||
b.Logout()
|
b.Logout()
|
||||||
{
|
{
|
||||||
notifies.drain(0)
|
nn := notifies.drain(1)
|
||||||
|
c.Assert(nn[0].Prefs, qt.IsNotNil) // emptyPrefs
|
||||||
// BUG: the backend has already called StartLogout, and we're
|
// BUG: the backend has already called StartLogout, and we're
|
||||||
// still logged out. So it shouldn't call it again.
|
// still logged out. So it shouldn't call it again.
|
||||||
cc.assertCalls("StartLogout", "unpause")
|
cc.assertCalls("StartLogout", "unpause")
|
||||||
@ -620,7 +631,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.send(nil, "", false, nil)
|
cc.send(nil, "", false, nil)
|
||||||
{
|
{
|
||||||
notifies.drain(0)
|
notifies.drain(0)
|
||||||
cc.assertCalls("unpause", "unpause")
|
cc.assertCalls()
|
||||||
c.Assert(b.Prefs().LoggedOut(), qt.IsTrue)
|
c.Assert(b.Prefs().LoggedOut(), qt.IsTrue)
|
||||||
c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
|
c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
|
||||||
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
||||||
@ -647,7 +658,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
cc.send(nil, "", false, nil)
|
cc.send(nil, "", false, nil)
|
||||||
{
|
{
|
||||||
notifies.drain(0)
|
notifies.drain(0)
|
||||||
cc.assertCalls("unpause", "unpause")
|
cc.assertCalls()
|
||||||
c.Assert(b.Prefs().LoggedOut(), qt.IsTrue)
|
c.Assert(b.Prefs().LoggedOut(), qt.IsTrue)
|
||||||
c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
|
c.Assert(b.Prefs().WantRunning(), qt.IsFalse)
|
||||||
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
||||||
@ -679,10 +690,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
c.Assert(ipn.NeedsLogin, qt.Equals, b.State())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's break the rules a little. Our control server accepts
|
b.Login(nil)
|
||||||
// your invalid login attempt, with no need for an interactive login.
|
|
||||||
// (This simulates an admin reviving a key that you previously
|
|
||||||
// disabled.)
|
|
||||||
t.Logf("\n\nLoginFinished3")
|
t.Logf("\n\nLoginFinished3")
|
||||||
notifies.expect(3)
|
notifies.expect(3)
|
||||||
cc.setAuthBlocked(false)
|
cc.setAuthBlocked(false)
|
||||||
@ -692,9 +700,10 @@ func TestStateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
nn := notifies.drain(3)
|
nn := notifies.drain(3)
|
||||||
cc.assertCalls("unpause", "unpause", "unpause")
|
cc.assertCalls("Login", "unpause", "unpause", "unpause")
|
||||||
c.Assert(nn[0].LoginFinished, qt.IsNotNil)
|
c.Assert(nn[0].LoginFinished, qt.IsNotNil)
|
||||||
c.Assert(nn[1].Prefs, qt.IsNotNil)
|
c.Assert(nn[1].Prefs, qt.IsNotNil)
|
||||||
|
c.Assert(nn[1].Prefs.Persist(), qt.IsNotNil)
|
||||||
c.Assert(nn[2].State, qt.IsNotNil)
|
c.Assert(nn[2].State, qt.IsNotNil)
|
||||||
// Prefs after finishing the login, so LoginName updated.
|
// Prefs after finishing the login, so LoginName updated.
|
||||||
c.Assert(nn[1].Prefs.Persist().LoginName, qt.Equals, "user2")
|
c.Assert(nn[1].Prefs.Persist().LoginName, qt.Equals, "user2")
|
||||||
@ -737,7 +746,7 @@ func TestStateMachine(t *testing.T) {
|
|||||||
c.Assert(nn[1].State, qt.IsNotNil)
|
c.Assert(nn[1].State, qt.IsNotNil)
|
||||||
c.Assert(nn[0].Prefs.WantRunning(), qt.IsFalse)
|
c.Assert(nn[0].Prefs.WantRunning(), qt.IsFalse)
|
||||||
c.Assert(nn[0].Prefs.LoggedOut(), qt.IsFalse)
|
c.Assert(nn[0].Prefs.LoggedOut(), qt.IsFalse)
|
||||||
c.Assert(ipn.Stopped, qt.Equals, *nn[1].State)
|
c.Assert(*nn[1].State, qt.Equals, ipn.Stopped)
|
||||||
}
|
}
|
||||||
|
|
||||||
// When logged in but !WantRunning, ipn leaves us unpaused to retrieve
|
// When logged in but !WantRunning, ipn leaves us unpaused to retrieve
|
||||||
|
@ -514,6 +514,7 @@ func TestLogoutRemovesAllPeers(t *testing.T) {
|
|||||||
nodes[i].AwaitIP()
|
nodes[i].AwaitIP()
|
||||||
nodes[i].AwaitRunning()
|
nodes[i].AwaitRunning()
|
||||||
}
|
}
|
||||||
|
expectedPeers := len(nodes) - 1
|
||||||
|
|
||||||
// Make every node ping every other node.
|
// Make every node ping every other node.
|
||||||
// This makes sure magicsock is fully populated.
|
// This makes sure magicsock is fully populated.
|
||||||
@ -543,12 +544,15 @@ func TestLogoutRemovesAllPeers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wantNode0PeerCount(len(nodes) - 1) // all other nodes are peers
|
wantNode0PeerCount(expectedPeers) // all other nodes are peers
|
||||||
nodes[0].MustLogOut()
|
nodes[0].MustLogOut()
|
||||||
wantNode0PeerCount(0) // node[0] is logged out, so it should not have any peers
|
wantNode0PeerCount(0) // node[0] is logged out, so it should not have any peers
|
||||||
nodes[0].MustUp()
|
|
||||||
|
nodes[0].MustUp() // This will create a new node
|
||||||
|
expectedPeers++
|
||||||
|
|
||||||
nodes[0].AwaitIP()
|
nodes[0].AwaitIP()
|
||||||
wantNode0PeerCount(len(nodes) - 1) // all other nodes are peers again
|
wantNode0PeerCount(expectedPeers) // all existing peers and the new node
|
||||||
}
|
}
|
||||||
|
|
||||||
// testEnv contains the test environment (set of servers) used by one
|
// testEnv contains the test environment (set of servers) used by one
|
||||||
|
Loading…
x
Reference in New Issue
Block a user