github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/track_test.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package engine
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  
    10  	"github.com/keybase/client/go/libkb"
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func runTrack(tc libkb.TestContext, fu *FakeUser, username string, sigVersion libkb.SigVersion) (idUI *FakeIdentifyUI, them *libkb.User, err error) {
    16  	sv := keybase1.SigVersion(sigVersion)
    17  	return runTrackWithOptions(tc, fu, username, keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, fu.NewSecretUI(), false)
    18  }
    19  
    20  func runTrackWithOptions(tc libkb.TestContext, fu *FakeUser, username string, options keybase1.TrackOptions, secretUI libkb.SecretUI, forceRemoteCheck bool) (idUI *FakeIdentifyUI, them *libkb.User, err error) {
    21  	idUI = &FakeIdentifyUI{}
    22  
    23  	arg := &TrackEngineArg{
    24  		UserAssertion:    username,
    25  		Options:          options,
    26  		ForceRemoteCheck: forceRemoteCheck,
    27  	}
    28  	uis := libkb.UIs{
    29  		LogUI:      tc.G.UI.GetLogUI(),
    30  		IdentifyUI: idUI,
    31  		SecretUI:   secretUI,
    32  	}
    33  	eng := NewTrackEngine(tc.G, arg)
    34  	m := NewMetaContextForTest(tc).WithUIs(uis)
    35  	err = RunEngine2(m, eng)
    36  	them = eng.User()
    37  	return
    38  }
    39  
    40  func assertTracking(tc libkb.TestContext, username string) {
    41  	me, err := libkb.LoadMe(libkb.NewLoadUserArg(tc.G))
    42  	require.NoError(tc.T, err)
    43  
    44  	them, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(tc.G, username))
    45  	require.NoError(tc.T, err)
    46  
    47  	m := NewMetaContextForTest(tc)
    48  	s, err := me.TrackChainLinkFor(m, them.GetNormalizedName(), them.GetUID())
    49  	require.NoError(tc.T, err)
    50  	require.NotNil(tc.T, s)
    51  }
    52  
    53  func assertNotTracking(tc libkb.TestContext, username string) {
    54  	me, err := libkb.LoadMe(libkb.NewLoadUserArg(tc.G))
    55  	require.NoError(tc.T, err)
    56  
    57  	them, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(tc.G, username))
    58  	require.NoError(tc.T, err)
    59  
    60  	m := NewMetaContextForTest(tc)
    61  	s, err := me.TrackChainLinkFor(m, them.GetNormalizedName(), them.GetUID())
    62  	require.NoError(tc.T, err)
    63  	require.Nil(tc.T, s)
    64  }
    65  
    66  func trackAlice(tc libkb.TestContext, fu *FakeUser, sigVersion libkb.SigVersion) {
    67  	sv := keybase1.SigVersion(sigVersion)
    68  	trackAliceWithOptions(tc, fu, keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, fu.NewSecretUI())
    69  }
    70  
    71  func trackUser(tc libkb.TestContext, fu *FakeUser, un libkb.NormalizedUsername, sigVersion libkb.SigVersion) {
    72  	sv := keybase1.SigVersion(sigVersion)
    73  	_, _, err := runTrackWithOptions(tc, fu, un.String(), keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, fu.NewSecretUI(), false)
    74  	require.NoError(tc.T, err)
    75  }
    76  
    77  func trackUserGetUI(tc libkb.TestContext, fu *FakeUser, un libkb.NormalizedUsername, sigVersion libkb.SigVersion) *FakeIdentifyUI {
    78  	sv := keybase1.SigVersion(sigVersion)
    79  	ui, _, err := runTrackWithOptions(tc, fu, un.String(), keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, fu.NewSecretUI(), false)
    80  	require.NoError(tc.T, err)
    81  	return ui
    82  }
    83  
    84  func trackAliceWithOptions(tc libkb.TestContext, fu *FakeUser, options keybase1.TrackOptions, secretUI libkb.SecretUI) {
    85  	idUI, res, err := runTrackWithOptions(tc, fu, "t_alice", options, secretUI, false)
    86  	require.NoError(tc.T, err)
    87  	upk, err := res.ExportToUPKV2AllIncarnations()
    88  	require.NoError(tc.T, err)
    89  	checkAliceProofs(tc.T, idUI, &upk.Current)
    90  	assertTracking(tc, "t_alice")
    91  }
    92  
    93  func trackBob(tc libkb.TestContext, fu *FakeUser, sigVersion libkb.SigVersion) {
    94  	sv := keybase1.SigVersion(sigVersion)
    95  	trackBobWithOptions(tc, fu, keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, fu.NewSecretUI())
    96  }
    97  
    98  func trackBobWithOptions(tc libkb.TestContext, fu *FakeUser, options keybase1.TrackOptions, secretUI libkb.SecretUI) {
    99  	// Refer to t_bob as kbtester1@twitter. This helps test a different
   100  	// codepath through idenfity2. (For example, in one case it triggered a
   101  	// race condition that aborted tracking without waiting for the UI to
   102  	// confirm, which wasn't present in the regular "t_bob" case.)
   103  
   104  	idUI, res, err := runTrackWithOptions(tc, fu, "kbtester1@twitter", options, secretUI, false)
   105  	require.NoError(tc.T, err)
   106  	upk, err := res.ExportToUPKV2AllIncarnations()
   107  	require.NoError(tc.T, err)
   108  	checkBobProofs(tc.T, idUI, &upk.Current)
   109  	assertTracking(tc, "t_bob")
   110  }
   111  
   112  func TestTrack(t *testing.T) {
   113  	doWithSigChainVersions(func(sigVersion libkb.SigVersion) {
   114  		_testTrack(t, sigVersion)
   115  	})
   116  }
   117  
   118  func _testTrack(t *testing.T, sigVersion libkb.SigVersion) {
   119  	tc := SetupEngineTest(t, "track")
   120  	defer tc.Cleanup()
   121  	fu := CreateAndSignupFakeUser(tc, "track")
   122  
   123  	trackAlice(tc, fu, sigVersion)
   124  	defer untrackAlice(tc, fu, sigVersion)
   125  
   126  	// Assert that we gracefully handle the case of no login
   127  	Logout(tc)
   128  	_, _, err := runTrack(tc, fu, "t_bob", sigVersion)
   129  	require.Error(t, err)
   130  	_, ok := err.(libkb.DeviceRequiredError)
   131  	require.True(t, ok)
   132  
   133  	fu.LoginOrBust(tc)
   134  	trackBob(tc, fu, sigVersion)
   135  	defer untrackBob(tc, fu, sigVersion)
   136  
   137  	// try tracking a user with no keys (which is now allowed)
   138  	_, _, err = runTrack(tc, fu, "t_ellen", sigVersion)
   139  	require.NoError(t, err)
   140  }
   141  
   142  // tests tracking a user that doesn't have a public key (#386)
   143  func TestTrackNoPubKey(t *testing.T) {
   144  	tc := SetupEngineTest(t, "track")
   145  	defer tc.Cleanup()
   146  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   147  	fu := CreateAndSignupFakeUser(tc, "track")
   148  	Logout(tc)
   149  
   150  	tracker := CreateAndSignupFakeUser(tc, "track")
   151  	_, _, err := runTrack(tc, tracker, fu.Username, sigVersion)
   152  	require.NoError(t, err)
   153  }
   154  
   155  func TestTrackMultiple(t *testing.T) {
   156  	tc := SetupEngineTest(t, "track")
   157  	defer tc.Cleanup()
   158  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   159  	fu := CreateAndSignupFakeUser(tc, "track")
   160  
   161  	trackAlice(tc, fu, sigVersion)
   162  	defer untrackAlice(tc, fu, sigVersion)
   163  
   164  	trackAlice(tc, fu, sigVersion)
   165  }
   166  
   167  func TestTrackNewUserWithPGP(t *testing.T) {
   168  	tc := SetupEngineTest(t, "track")
   169  	defer tc.Cleanup()
   170  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   171  	fu := createFakeUserWithPGPSibkey(tc)
   172  	Logout(tc)
   173  
   174  	tracker := CreateAndSignupFakeUser(tc, "track")
   175  	t.Logf("first track:")
   176  	_, _, err := runTrack(tc, tracker, fu.Username, sigVersion)
   177  	require.NoError(t, err)
   178  
   179  	t.Logf("second track:")
   180  	_, _, err = runTrack(tc, tracker, fu.Username, sigVersion)
   181  	require.NoError(t, err)
   182  }
   183  
   184  // see issue #578
   185  func TestTrackRetrack(t *testing.T) {
   186  
   187  	tc := SetupEngineTest(t, "track")
   188  	defer tc.Cleanup()
   189  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   190  	fu := createFakeUserWithPGPSibkey(tc)
   191  
   192  	idUI := &FakeIdentifyUI{}
   193  	secretUI := fu.NewSecretUI()
   194  
   195  	var err error
   196  	fu.User, err = libkb.LoadMe(libkb.NewLoadUserPubOptionalArg(tc.G))
   197  	require.NoError(t, err)
   198  	seqnoBefore := fu.User.GetSigChainLastKnownSeqno()
   199  
   200  	sv := keybase1.SigVersion(sigVersion)
   201  	arg := &TrackEngineArg{
   202  		UserAssertion: "t_alice",
   203  		Options:       keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv},
   204  	}
   205  	uis := libkb.UIs{
   206  		LogUI:      tc.G.UI.GetLogUI(),
   207  		IdentifyUI: idUI,
   208  		SecretUI:   secretUI,
   209  	}
   210  	eng := NewTrackEngine(tc.G, arg)
   211  	m := NewMetaContextForTest(tc).WithUIs(uis)
   212  	err = RunEngine2(m, eng)
   213  	require.NoError(t, err)
   214  
   215  	fu.User, err = libkb.LoadMe(libkb.NewLoadUserPubOptionalArg(tc.G))
   216  	require.NoError(t, err)
   217  	seqnoAfter := fu.User.GetSigChainLastKnownSeqno()
   218  
   219  	require.NotEqual(t, seqnoAfter, seqnoBefore)
   220  
   221  	eng = NewTrackEngine(tc.G, arg)
   222  	err = RunEngine2(m, eng)
   223  	require.NoError(t, err)
   224  
   225  	fu.User, err = libkb.LoadMe(libkb.NewLoadUserPubOptionalArg(tc.G))
   226  	require.NoError(t, err)
   227  	seqnoRetrack := fu.User.GetSigChainLastKnownSeqno()
   228  
   229  	require.False(t, seqnoRetrack > seqnoAfter)
   230  }
   231  
   232  func TestTrackLocal(t *testing.T) {
   233  	tc := SetupEngineTest(t, "track")
   234  	defer tc.Cleanup()
   235  	fu := CreateAndSignupFakeUser(tc, "track")
   236  
   237  	_, them, err := runTrackWithOptions(tc, fu, "t_alice", keybase1.TrackOptions{LocalOnly: true, BypassConfirm: true}, fu.NewSecretUI(), false)
   238  	require.NoError(t, err)
   239  
   240  	require.NotNil(t, them)
   241  
   242  	me, err := libkb.LoadMe(libkb.NewLoadUserArg(tc.G))
   243  	require.NoError(t, err)
   244  
   245  	m := NewMetaContextForTest(tc)
   246  	s, err := me.TrackChainLinkFor(m, them.GetNormalizedName(), them.GetUID())
   247  	require.NoError(t, err)
   248  	require.NotNil(t, s)
   249  	require.False(t, s.IsRemote())
   250  }
   251  
   252  // Make sure the track engine uses the secret store.
   253  func TestTrackWithSecretStore(t *testing.T) {
   254  	doWithSigChainVersions(func(sigVersion libkb.SigVersion) {
   255  		_testTrackWithSecretStore(t, sigVersion)
   256  	})
   257  }
   258  
   259  func _testTrackWithSecretStore(t *testing.T, sigVersion libkb.SigVersion) {
   260  	testEngineWithSecretStore(t, func(
   261  		tc libkb.TestContext, fu *FakeUser, secretUI libkb.SecretUI) {
   262  		trackAliceWithOptions(tc, fu, keybase1.TrackOptions{BypassConfirm: true}, secretUI)
   263  		untrackAlice(tc, fu, sigVersion)
   264  	})
   265  }
   266  
   267  // Test for Core-2196 identify/track race detection
   268  func TestIdentifyTrackRaceDetection(t *testing.T) {
   269  	doWithSigChainVersions(func(sigVersion libkb.SigVersion) {
   270  		_testIdentifyTrackRaceDetection(t, sigVersion)
   271  	})
   272  }
   273  
   274  func _testIdentifyTrackRaceDetection(t *testing.T, sigVersion libkb.SigVersion) {
   275  	user, dev1, dev2, cleanup := SetupTwoDevices(t, "track")
   276  	defer cleanup()
   277  
   278  	trackee := "t_tracy"
   279  
   280  	doID := func(tc libkb.TestContext, fui *FakeIdentifyUI) {
   281  
   282  		iarg := &keybase1.Identify2Arg{
   283  			UserAssertion: trackee,
   284  			// We need to block on identification so that the track token
   285  			// is delivered to the UI before we return. Otherwise, the
   286  			// following call to track might happen before the token
   287  			// is known.
   288  			AlwaysBlock:      true,
   289  			IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
   290  		}
   291  		eng := NewResolveThenIdentify2(tc.G, iarg)
   292  		uis := libkb.UIs{IdentifyUI: fui}
   293  		m := NewMetaContextForTest(tc).WithUIs(uis)
   294  		err := RunEngine2(m, eng)
   295  		require.NoError(t, err)
   296  	}
   297  
   298  	sv := keybase1.SigVersion(sigVersion)
   299  	track := func(tc libkb.TestContext, fui *FakeIdentifyUI) error {
   300  		arg := TrackTokenArg{
   301  			Token: fui.Token,
   302  			Options: keybase1.TrackOptions{
   303  				BypassConfirm: true,
   304  				ForceRetrack:  true,
   305  				SigVersion:    &sv,
   306  			},
   307  		}
   308  		uis := libkb.UIs{
   309  			LogUI:    tc.G.UI.GetLogUI(),
   310  			SecretUI: user.NewSecretUI(),
   311  		}
   312  		eng := NewTrackToken(tc.G, &arg)
   313  		m := NewMetaContextForTest(tc).WithUIs(uis)
   314  		return RunEngine2(m, eng)
   315  	}
   316  
   317  	trackSucceed := func(tc libkb.TestContext, fui *FakeIdentifyUI) {
   318  		err := track(tc, fui)
   319  		require.NoError(tc.T, err)
   320  		assertTracking(dev1, trackee)
   321  	}
   322  
   323  	trackFail := func(tc libkb.TestContext, fui *FakeIdentifyUI, firstTrack bool) {
   324  		err := track(tc, fui)
   325  		require.Error(tc.T, err)
   326  		tse, ok := err.(libkb.TrackStaleError)
   327  		require.True(tc.T, ok)
   328  		require.Equal(tc.T, tse.FirstTrack, firstTrack)
   329  	}
   330  
   331  	for i := 0; i < 2; i++ {
   332  		fui1 := &FakeIdentifyUI{}
   333  		fui2 := &FakeIdentifyUI{}
   334  		doID(dev1, fui1)
   335  		if i > 0 {
   336  			// Device2 won't know that device1 made a change to the ME user
   337  			// in time to make this test pass. So we hack in an invalidation.
   338  			// We might have used the fact the userchanged notifications are bounced
   339  			// off of the server, but that might slow down this test, so do the
   340  			// simple and non-flakey thing.
   341  			dev2.G.GetUPAKLoader().Invalidate(context.TODO(), libkb.UsernameToUID(user.Username))
   342  		}
   343  		doID(dev2, fui2)
   344  		trackSucceed(dev1, fui1)
   345  		trackFail(dev2, fui2, (i == 0))
   346  	}
   347  
   348  	err := runUntrack(dev1, user, trackee, sigVersion)
   349  	require.NoError(t, err)
   350  }
   351  
   352  func TestTrackNoKeys(t *testing.T) {
   353  	tc := SetupEngineTest(t, "track")
   354  	defer tc.Cleanup()
   355  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   356  	nk, pp := createFakeUserWithNoKeys(tc)
   357  	Logout(tc)
   358  
   359  	fu := CreateAndSignupFakeUser(tc, "track")
   360  	trackUser(tc, fu, libkb.NewNormalizedUsername(nk), sigVersion)
   361  
   362  	// provision nk on a new device
   363  	Logout(tc)
   364  	nku := &FakeUser{Username: nk, Passphrase: pp}
   365  	err := nku.Login(tc.G)
   366  	require.NoError(t, err)
   367  	Logout(tc)
   368  
   369  	// track nk again
   370  	err = fu.Login(tc.G)
   371  	require.NoError(t, err)
   372  	ui := trackUserGetUI(tc, fu, libkb.NewNormalizedUsername(nk), sigVersion)
   373  
   374  	// ensure track diff for new eldest key
   375  	require.Equal(t, 1, len(ui.DisplayKeyDiffs))
   376  	require.Equal(t, ui.DisplayKeyDiffs[0].Type, keybase1.TrackDiffType_NEW_ELDEST)
   377  }
   378  
   379  func TestTrackSelf(t *testing.T) {
   380  	tc := SetupEngineTest(t, "track")
   381  	defer tc.Cleanup()
   382  
   383  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   384  	sv := keybase1.SigVersion(sigVersion)
   385  	fu := CreateAndSignupFakeUser(tc, "track")
   386  	_, _, err := runTrackWithOptions(tc, fu, fu.NormalizedUsername().String(), keybase1.TrackOptions{
   387  		BypassConfirm: true,
   388  		SigVersion:    &sv,
   389  	}, fu.NewSecretUI(), false)
   390  	require.Error(t, err)
   391  	require.Equal(t, "You can't follow yourself.", err.Error())
   392  }