github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/track_token_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 "testing" 8 "time" 9 10 "github.com/keybase/client/go/gregor" 11 "github.com/keybase/client/go/libkb" 12 gregor1 "github.com/keybase/client/go/protocol/gregor1" 13 keybase1 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/keybase/clockwork" 15 context "golang.org/x/net/context" 16 ) 17 18 func doWithSigChainVersions(f func(libkb.SigVersion)) { 19 f(libkb.KeybaseSignatureV1) 20 f(libkb.KeybaseSignatureV2) 21 } 22 23 func TestTrackTokenIdentify2(t *testing.T) { 24 doWithSigChainVersions(func(sigVersion libkb.SigVersion) { 25 _testTrackTokenIdentify2(t, sigVersion) 26 }) 27 } 28 29 func _testTrackTokenIdentify2(t *testing.T, sigVersion libkb.SigVersion) { 30 tc := SetupEngineTest(t, "track") 31 defer tc.Cleanup() 32 fu := CreateAndSignupFakeUser(tc, "track") 33 34 idUI := &FakeIdentifyUI{} 35 username := "t_tracy" 36 arg := &keybase1.Identify2Arg{ 37 UserAssertion: username, 38 NeedProofSet: true, 39 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI, 40 } 41 uis := libkb.UIs{ 42 LogUI: tc.G.UI.GetLogUI(), 43 IdentifyUI: idUI, 44 SecretUI: fu.NewSecretUI(), 45 } 46 eng := NewResolveThenIdentify2(tc.G, arg) 47 m := NewMetaContextForTest(tc).WithUIs(uis) 48 if err := RunEngine2(m, eng); err != nil { 49 tc.T.Fatal(err) 50 } 51 sv := keybase1.SigVersion(sigVersion) 52 targ := TrackTokenArg{ 53 Token: idUI.Token, 54 Options: keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, 55 } 56 teng := NewTrackToken(tc.G, &targ) 57 if err := RunEngine2(m, teng); err != nil { 58 tc.T.Fatal(err) 59 } 60 61 defer func() { _ = runUntrack(tc, fu, username, sigVersion) }() 62 assertTracking(tc, username) 63 } 64 65 func TestTrackLocalThenLocalTemp(t *testing.T) { 66 tc := SetupEngineTest(t, "track") 67 defer tc.Cleanup() 68 sigVersion := libkb.GetDefaultSigVersion(tc.G) 69 70 fakeClock := clockwork.NewFakeClockAt(time.Now()) 71 tc.G.SetClock(fakeClock) 72 fu := CreateAndSignupFakeUser(tc, "track") 73 74 flakeyAPI := flakeyRooterAPI{orig: tc.G.XAPI, flakeOut: false, G: tc.G} 75 tc.G.XAPI = &flakeyAPI 76 77 idUI := &FakeIdentifyUI{} 78 username := "t_tracy" 79 80 arg := &keybase1.Identify2Arg{ 81 UserAssertion: username, 82 NeedProofSet: true, 83 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI, 84 } 85 uis := libkb.UIs{ 86 LogUI: tc.G.UI.GetLogUI(), 87 IdentifyUI: idUI, 88 SecretUI: fu.NewSecretUI(), 89 } 90 91 // Identify tracy; all proofs should work 92 eng := NewResolveThenIdentify2(tc.G, arg) 93 m := NewMetaContextForTest(tc).WithUIs(uis) 94 if err := RunEngine2(m, eng); err != nil { 95 t.Fatal(err) 96 } 97 sv := keybase1.SigVersion(sigVersion) 98 targ := TrackTokenArg{ 99 Token: idUI.Token, 100 Options: keybase1.TrackOptions{BypassConfirm: true, LocalOnly: true, SigVersion: &sv}, 101 } 102 103 // Track tracy 104 teng := NewTrackToken(tc.G, &targ) 105 if err := RunEngine2(m, teng); err != nil { 106 t.Fatal(err) 107 } 108 109 defer func() { _ = runUntrack(tc, fu, username, sigVersion) }() 110 111 // Now make her Rooter proof fail with a 429 112 flakeyAPI.flakeOut = true 113 idUI = &FakeIdentifyUI{} 114 m = m.WithIdentifyUI(idUI) 115 116 // Advance so that our previous cached success is out of 117 // cache 118 fakeClock.Advance(tc.G.Env.GetProofCacheLongDur() + time.Minute) 119 120 eng = NewResolveThenIdentify2(tc.G, arg) 121 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 122 123 // Should get an error 124 if err := RunEngine2(m, eng); err == nil { 125 t.Fatal("Expected identify error") 126 } 127 128 result, found := idUI.ProofResults["rooter"] 129 if !found { 130 t.Fatal("Failed to find a rooter proof") 131 } 132 if pe := libkb.ImportProofError(result.ProofResult); pe == nil { 133 t.Fatal("expected a Rooter error result") 134 } 135 136 // This is like the UI saying to store the local track 137 targ.Options.ExpiringLocal = true 138 targ.Token = idUI.Token 139 // Track tracy 140 teng = NewTrackToken(tc.G, &targ) 141 if err := RunEngine2(m, teng); err != nil { 142 t.Fatal(err) 143 } 144 145 // Identify should work once more because we signed with failures 146 eng = NewResolveThenIdentify2(tc.G, arg) 147 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 148 var err error 149 // Should not get an error 150 if err = RunEngine2(m, eng); err != nil { 151 t.Logf("Identify failure: %v", err) 152 t.Fatal("Expected to pass identify") 153 } 154 155 result, found = idUI.ProofResults["rooter"] 156 if !found { 157 t.Fatal("Failed to find a rooter proof") 158 } 159 if result.Diff == nil { 160 t.Fatal("Failed to find a rooter proof result diff") 161 } 162 if result.Diff.Type != keybase1.TrackDiffType_NONE_VIA_TEMPORARY { 163 t.Fatal("Failed to find a rooter proof result diff of type TrackDiffType_NONE_VIA_TEMPORARY") 164 } 165 if pe := libkb.ImportProofError(result.ProofResult); pe == nil { 166 t.Fatal("expected a Rooter error result") 167 } 168 169 // Advance so that our temporary track is discarded 170 fakeClock.Advance(tc.G.Env.GetLocalTrackMaxAge() + time.Minute) 171 172 // Identify should fail once more 173 eng = NewResolveThenIdentify2(tc.G, arg) 174 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 175 // Should get an error 176 if err = RunEngine2(m, eng); err == nil { 177 t.Fatal("Expected rooter to fail") 178 } 179 t.Logf("Identify failure: %v", err) 180 181 result, found = idUI.ProofResults["rooter"] 182 if !found { 183 t.Fatal("Failed to find a rooter proof") 184 } 185 if pe := libkb.ImportProofError(result.ProofResult); pe == nil { 186 t.Fatal("expected a Rooter error result") 187 } 188 189 assertTracking(tc, username) 190 } 191 192 func TestTrackRemoteThenLocalTemp(t *testing.T) { 193 doWithSigChainVersions(func(sigVersion libkb.SigVersion) { 194 _testTrackRemoteThenLocalTemp(t, sigVersion) 195 }) 196 } 197 198 func _testTrackRemoteThenLocalTemp(t *testing.T, sigVersion libkb.SigVersion) { 199 tc := SetupEngineTest(t, "track") 200 defer tc.Cleanup() 201 202 // Tracking remote means we have to agree what time it is 203 fakeClock := clockwork.NewFakeClockAt(time.Now()) 204 tc.G.SetClock(fakeClock) 205 fu := CreateAndSignupFakeUser(tc, "track") 206 207 flakeyAPI := flakeyRooterAPI{orig: tc.G.XAPI, flakeOut: false, G: tc.G} 208 tc.G.XAPI = &flakeyAPI 209 210 idUI := &FakeIdentifyUI{} 211 username := "t_tracy" 212 213 arg := &keybase1.Identify2Arg{ 214 UserAssertion: username, 215 NeedProofSet: true, 216 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI, 217 } 218 uis := libkb.UIs{ 219 LogUI: tc.G.UI.GetLogUI(), 220 IdentifyUI: idUI, 221 SecretUI: fu.NewSecretUI(), 222 } 223 224 // Identify tracy; all proofs should work 225 eng := NewResolveThenIdentify2(tc.G, arg) 226 m := NewMetaContextForTest(tc).WithUIs(uis) 227 if err := RunEngine2(m, eng); err != nil { 228 t.Fatal(err) 229 } 230 // Leaving LocalOnly off here will result in remote tracking 231 sv := keybase1.SigVersion(sigVersion) 232 targ := TrackTokenArg{ 233 Token: idUI.Token, 234 Options: keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, 235 } 236 237 // Track tracy 238 teng := NewTrackToken(tc.G, &targ) 239 if err := RunEngine2(m, teng); err != nil { 240 t.Fatal(err) 241 } 242 243 defer func() { _ = runUntrack(tc, fu, username, sigVersion) }() 244 245 // Now make her Rooter proof fail with a 429 246 flakeyAPI.flakeOut = true 247 idUI = &FakeIdentifyUI{} 248 m = m.WithIdentifyUI(idUI) 249 250 // Advance so that our previous cached success is out of 251 // cache 252 fakeClock.Advance(tc.G.Env.GetProofCacheLongDur() + time.Minute) 253 254 eng = NewResolveThenIdentify2(tc.G, arg) 255 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 256 257 // Should get an error 258 if err := RunEngine2(m, eng); err == nil { 259 t.Fatal("Expected identify error") 260 } 261 262 result, found := idUI.ProofResults["rooter"] 263 if !found { 264 t.Fatal("Failed to find a rooter proof") 265 } 266 if pe := libkb.ImportProofError(result.ProofResult); pe == nil { 267 t.Fatal("expected a Rooter error result") 268 } 269 270 // This is like the UI saying to store the local track 271 targ.Options.ExpiringLocal = true 272 targ.Token = idUI.Token 273 // Track tracy 274 teng = NewTrackToken(tc.G, &targ) 275 if err := RunEngine2(m, teng); err != nil { 276 t.Fatal(err) 277 } 278 279 // Identify should work once more because we signed with failures 280 eng = NewResolveThenIdentify2(tc.G, arg) 281 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 282 var err error 283 // Should not get an error 284 if err = RunEngine2(m, eng); err != nil { 285 t.Logf("Identify failure: %v", err) 286 t.Fatal("Expected to pass identify") 287 } 288 289 result, found = idUI.ProofResults["rooter"] 290 if !found { 291 t.Fatal("Failed to find a rooter proof") 292 } 293 if pe := libkb.ImportProofError(result.ProofResult); pe == nil { 294 t.Fatal("expected a Rooter error result") 295 } 296 297 // Advance so that our temporary track is discarded 298 // cache 299 fakeClock.Advance(tc.G.Env.GetLocalTrackMaxAge() + time.Minute) 300 301 // Identify should fail once more 302 eng = NewResolveThenIdentify2(tc.G, arg) 303 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 304 // Should get an error 305 if err = RunEngine2(m, eng); err == nil { 306 t.Fatal("Expected rooter to fail") 307 } 308 t.Logf("Identify failure: %v", err) 309 310 result, found = idUI.ProofResults["rooter"] 311 if !found { 312 t.Fatal("Failed to find a rooter proof") 313 } 314 if pe := libkb.ImportProofError(result.ProofResult); pe == nil { 315 t.Fatal("expected a Rooter error result") 316 } 317 318 assertTracking(tc, username) 319 } 320 321 func TestTrackFailTempRecover(t *testing.T) { 322 tc := SetupEngineTest(t, "track") 323 defer tc.Cleanup() 324 sigVersion := libkb.GetDefaultSigVersion(tc.G) 325 326 fakeClock := clockwork.NewFakeClockAt(time.Now()) 327 tc.G.SetClock(fakeClock) 328 fu := CreateAndSignupFakeUser(tc, "track") 329 330 flakeyAPI := flakeyRooterAPI{orig: tc.G.XAPI, flakeOut: false, G: tc.G} 331 tc.G.XAPI = &flakeyAPI 332 333 idUI := &FakeIdentifyUI{} 334 username := "t_tracy" 335 336 arg := &keybase1.Identify2Arg{ 337 UserAssertion: username, 338 NeedProofSet: true, 339 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI, 340 } 341 uis := libkb.UIs{ 342 LogUI: tc.G.UI.GetLogUI(), 343 IdentifyUI: idUI, 344 SecretUI: fu.NewSecretUI(), 345 } 346 347 // Identify tracy; all proofs should work 348 eng := NewResolveThenIdentify2(tc.G, arg) 349 m := NewMetaContextForTest(tc).WithUIs(uis) 350 if err := RunEngine2(m, eng); err != nil { 351 t.Fatal(err) 352 } 353 sv := keybase1.SigVersion(sigVersion) 354 targ := TrackTokenArg{ 355 Token: idUI.Token, 356 Options: keybase1.TrackOptions{BypassConfirm: true, LocalOnly: true, SigVersion: &sv}, 357 } 358 359 // Track tracy 360 teng := NewTrackToken(tc.G, &targ) 361 if err := RunEngine2(m, teng); err != nil { 362 t.Fatal(err) 363 } 364 365 defer func() { _ = runUntrack(tc, fu, username, sigVersion) }() 366 367 // Now make her Rooter proof fail with a 429 368 flakeyAPI.flakeOut = true 369 idUI = &FakeIdentifyUI{} 370 m = m.WithIdentifyUI(idUI) 371 372 // Advance so that our previous cached success is out of 373 // cache 374 fakeClock.Advance(tc.G.Env.GetProofCacheLongDur() + time.Minute) 375 376 eng = NewResolveThenIdentify2(tc.G, arg) 377 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 378 379 // Should get an error 380 if err := RunEngine2(m, eng); err == nil { 381 t.Fatal("Expected identify error") 382 } 383 384 result, found := idUI.ProofResults["rooter"] 385 if !found { 386 t.Fatal("Failed to find a rooter proof") 387 } 388 pe := libkb.ImportProofError(result.ProofResult) 389 if pe == nil { 390 t.Fatal("expected a Rooter error result") 391 } 392 393 t.Logf("Rooter proof result, error: %v, -- %v", result, pe) 394 if result.Diff != nil { 395 t.Logf("Rooter proof result diff: %v", result.Diff) 396 } 397 // This is like the UI saying to store the local track 398 targ.Options.ExpiringLocal = true 399 400 targ.Token = idUI.Token 401 // Track tracy 402 teng = NewTrackToken(tc.G, &targ) 403 if err := RunEngine2(m, teng); err != nil { 404 t.Fatal(err) 405 } 406 407 // Now make her Rooter proof work again 408 flakeyAPI.flakeOut = false 409 410 // Identify should work because of the original, permanent track 411 eng = NewResolveThenIdentify2(tc.G, arg) 412 eng.testArgs = &Identify2WithUIDTestArgs{noCache: true} 413 var err error 414 // Should not get an error 415 if err = RunEngine2(m, eng); err != nil { 416 t.Logf("Identify failure: %v", err) 417 t.Fatal("Expected to pass identify") 418 } 419 420 // There shouldn't be a Diff in the result, but if there is, make sure 421 // it isn't due to temporary tracking 422 result, found = idUI.ProofResults["rooter"] 423 if !found || result.Diff.Type != keybase1.TrackDiffType_NONE { 424 t.Fatalf("Expected a TrackDiffType_NONE") 425 } 426 427 // Advance the clock to make sure local temp track goes away 428 fakeClock.Advance(tc.G.Env.GetLocalTrackMaxAge() + time.Minute) 429 430 if err := eng.i2eng.createIdentifyState(m); err != nil { 431 t.Fatal(err) 432 } 433 if eng.i2eng.state.TrackLookup() == nil { 434 t.Fatalf("Expected permanent LocalTrackChainLinkFor %s", username) 435 } 436 if eng.i2eng.state.TmpTrackLookup() != nil { 437 t.Fatalf("Expected no temporary LocalTrackChainLinkFor %s", username) 438 } 439 assertTracking(tc, username) 440 } 441 442 type FakeGregorState struct { 443 dismissedMsgID gregor.MsgID 444 } 445 446 var _ libkb.GregorState = (*FakeGregorState)(nil) 447 448 func (d *FakeGregorState) State(ctx context.Context) (gregor.State, error) { 449 return nil, nil 450 } 451 452 func (d *FakeGregorState) UpdateCategory(ctx context.Context, cat string, body []byte, 453 dtime gregor1.TimeOrOffset) (res gregor1.MsgID, err error) { 454 return gregor1.MsgID{}, nil 455 } 456 457 func (d *FakeGregorState) InjectItem(ctx context.Context, cat string, body []byte, dtime gregor1.TimeOrOffset) (gregor1.MsgID, error) { 458 return nil, nil 459 } 460 461 func (d *FakeGregorState) DismissItem(ctx context.Context, cli gregor1.IncomingInterface, id gregor.MsgID) error { 462 d.dismissedMsgID = id 463 return nil 464 } 465 466 func (d *FakeGregorState) DismissCategory(ct context.Context, cat gregor1.Category) error { 467 return nil 468 } 469 470 func (d *FakeGregorState) LocalDismissItem(ctx context.Context, id gregor.MsgID) error { 471 return nil 472 } 473 474 func TestTrackWithTokenDismissesGregor(t *testing.T) { 475 tc := SetupEngineTest(t, "track") 476 defer tc.Cleanup() 477 sigVersion := libkb.GetDefaultSigVersion(tc.G) 478 fu := CreateAndSignupFakeUser(tc, "track") 479 480 dismisser := &FakeGregorState{} 481 tc.G.GregorState = dismisser 482 483 msgID := gregor1.MsgID("my_random_id") 484 responsibleGregorItem := gregor1.ItemAndMetadata{ 485 // All we need for this test is the msgID, to check for dismissal. 486 Md_: &gregor1.Metadata{ 487 MsgID_: msgID, 488 }, 489 } 490 491 idUI := &FakeIdentifyUI{} 492 username := "t_tracy" 493 arg := &keybase1.Identify2Arg{ 494 UserAssertion: username, 495 NeedProofSet: true, 496 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI, 497 } 498 uis := libkb.UIs{ 499 LogUI: tc.G.UI.GetLogUI(), 500 IdentifyUI: idUI, 501 SecretUI: fu.NewSecretUI(), 502 } 503 eng := NewResolveThenIdentify2(tc.G, arg) 504 m := NewMetaContextForTest(tc).WithUIs(uis) 505 eng.SetResponsibleGregorItem(&responsibleGregorItem) 506 if err := RunEngine2(m, eng); err != nil { 507 tc.T.Fatal(err) 508 } 509 sv := keybase1.SigVersion(sigVersion) 510 targ := TrackTokenArg{ 511 Token: idUI.Token, 512 Options: keybase1.TrackOptions{BypassConfirm: true, SigVersion: &sv}, 513 } 514 teng := NewTrackToken(tc.G, &targ) 515 if err := RunEngine2(m, teng); err != nil { 516 tc.T.Fatal(err) 517 } 518 519 // Check that the dismissed ID matches what we defined above. 520 if msgID.String() != dismisser.dismissedMsgID.String() { 521 tc.T.Fatalf("Dismissed msgID (%s) != responsible msgID (%s)", msgID.String(), dismisser.dismissedMsgID.String()) 522 } 523 }