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 }