github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/tlfhandle/handle_test.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package tlfhandle 6 7 import ( 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/keybase/client/go/kbfs/idutil" 13 idutiltest "github.com/keybase/client/go/kbfs/idutil/test" 14 "github.com/keybase/client/go/kbfs/kbfscodec" 15 "github.com/keybase/client/go/kbfs/tlf" 16 kbname "github.com/keybase/client/go/kbun" 17 "github.com/keybase/client/go/protocol/keybase1" 18 "github.com/pkg/errors" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "golang.org/x/net/context" 22 ) 23 24 func TestParseHandleEarlyFailure(t *testing.T) { 25 ctx := context.Background() 26 27 name := "w1,w2#r1" 28 _, err := ParseHandle(ctx, nil, nil, nil, name, tlf.Public) 29 assert.Equal(t, idutil.NoSuchNameError{Name: name}, err) 30 31 nonCanonicalName := "W1,w2#r1" 32 _, err = ParseHandle(ctx, nil, nil, nil, nonCanonicalName, tlf.Private) 33 assert.Equal( 34 t, idutil.TlfNameNotCanonical{ 35 Name: nonCanonicalName, NameToTry: name}, errors.Cause(err)) 36 } 37 38 func TestParseHandleNoUserFailure(t *testing.T) { 39 ctx := context.Background() 40 41 localUsers := idutil.MakeLocalUsers( 42 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 43 currentUID := localUsers[0].UID 44 daemon := idutil.NewDaemonLocal( 45 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 46 47 kbpki := &idutiltest.IdentifyCountingKBPKI{ 48 KBPKI: &idutiltest.DaemonKBPKI{ 49 Daemon: daemon, 50 }, 51 } 52 53 name := "u2,u3#u4" 54 _, err := ParseHandle(ctx, kbpki, nil, nil, name, tlf.Private) 55 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 56 assert.Equal(t, idutil.NoSuchUserError{Input: "u4"}, err) 57 } 58 59 func TestParseHandleNotReaderFailure(t *testing.T) { 60 ctx := context.Background() 61 62 localUsers := idutil.MakeLocalUsers( 63 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 64 currentUID := localUsers[0].UID 65 daemon := idutil.NewDaemonLocal( 66 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 67 68 kbpki := &idutiltest.IdentifyCountingKBPKI{ 69 KBPKI: &idutiltest.DaemonKBPKI{ 70 Daemon: daemon, 71 }, 72 } 73 74 name := "u2,u3" 75 _, err := ParseHandle( 76 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, name, 77 tlf.Private) 78 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 79 assert.Equal(t, ReadAccessError{User: "u1", Tlf: tlf.CanonicalName(name), Type: tlf.Private, Filename: "/keybase/private/u2,u3"}, err) 80 } 81 82 func TestParseHandleSingleTeam(t *testing.T) { 83 ctx := context.Background() 84 85 localUsers := idutil.MakeLocalUsers([]kbname.NormalizedUsername{"u1"}) 86 currentUID := localUsers[0].UID 87 localTeams := idutil.MakeLocalTeams([]kbname.NormalizedUsername{"t1"}) 88 daemon := idutil.NewDaemonLocal( 89 currentUID, localUsers, localTeams, kbfscodec.NewMsgpack()) 90 91 tlfID := tlf.FakeID(0, tlf.SingleTeam) 92 err := daemon.CreateTeamTLF(ctx, localTeams[0].TID, tlfID) 93 require.NoError(t, err) 94 95 kbpki := &idutiltest.IdentifyCountingKBPKI{ 96 KBPKI: &idutiltest.DaemonKBPKI{ 97 Daemon: daemon, 98 }, 99 } 100 101 h, err := ParseHandle( 102 ctx, kbpki, ConstIDGetter{tlfID}, nil, "t1", tlf.SingleTeam) 103 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 104 require.NoError(t, err) 105 require.Equal(t, tlfID, h.tlfID) 106 } 107 108 func TestParseHandleSingleTeamFailures(t *testing.T) { 109 ctx := context.Background() 110 111 localUsers := idutil.MakeLocalUsers( 112 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 113 currentUID := localUsers[0].UID 114 localTeams := idutil.MakeLocalTeams( 115 []kbname.NormalizedUsername{"t1", "t2"}) 116 daemon := idutil.NewDaemonLocal( 117 currentUID, localUsers, localTeams, kbfscodec.NewMsgpack()) 118 119 kbpki := &idutiltest.IdentifyCountingKBPKI{ 120 KBPKI: &idutiltest.DaemonKBPKI{ 121 Daemon: daemon, 122 }, 123 } 124 125 _, err := ParseHandle(ctx, kbpki, nil, nil, "u1", tlf.SingleTeam) 126 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 127 assert.Equal(t, idutil.NoSuchUserError{Input: "u1@team"}, err) 128 129 checkNoSuchName := func(name string, ty tlf.Type) { 130 _, err := ParseHandle(ctx, kbpki, nil, nil, name, ty) 131 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 132 if ty == tlf.SingleTeam { 133 assert.Equal(t, idutil.NoSuchNameError{Name: name}, err) 134 } else { 135 assert.Equal(t, idutil.NoSuchUserError{Input: "t1"}, err) 136 } 137 } 138 139 checkNoSuchName("t1,u1", tlf.SingleTeam) 140 checkNoSuchName("u1,t1", tlf.SingleTeam) 141 checkNoSuchName("t1,t2", tlf.SingleTeam) 142 checkNoSuchName("t1#t2", tlf.SingleTeam) 143 checkNoSuchName("t1", tlf.Private) 144 checkNoSuchName("t1,u1", tlf.Private) 145 checkNoSuchName("u1#t1", tlf.Private) 146 checkNoSuchName("t1#u1", tlf.Private) 147 checkNoSuchName("t1", tlf.Public) 148 checkNoSuchName("t1,u1", tlf.Public) 149 } 150 151 func TestParseHandleAssertionNotCanonicalFailure(t *testing.T) { 152 ctx := context.Background() 153 154 localUsers := idutil.MakeLocalUsers( 155 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 156 localUsers[2].Asserts = []string{"u3@twitter"} 157 currentUID := localUsers[0].UID 158 daemon := idutil.NewDaemonLocal( 159 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 160 161 kbpki := &idutiltest.IdentifyCountingKBPKI{ 162 KBPKI: &idutiltest.DaemonKBPKI{ 163 Daemon: daemon, 164 }, 165 } 166 167 name := "u1,u3#u2" 168 nonCanonicalName := "u1,u3@twitter#u2" 169 _, err := ParseHandle( 170 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 171 nonCanonicalName, tlf.Private) 172 // Names with assertions should be identified before the error 173 // is returned. 174 assert.Equal(t, 3, kbpki.GetIdentifyCalls()) 175 assert.Equal( 176 t, idutil.TlfNameNotCanonical{Name: nonCanonicalName, NameToTry: name}, 177 errors.Cause(err)) 178 } 179 180 func TestParseHandleAssertionPrivateSuccess(t *testing.T) { 181 ctx := context.Background() 182 183 localUsers := idutil.MakeLocalUsers( 184 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 185 currentUID := localUsers[0].UID 186 daemon := idutil.NewDaemonLocal( 187 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 188 189 kbpki := &idutiltest.IdentifyCountingKBPKI{ 190 KBPKI: &idutiltest.DaemonKBPKI{ 191 Daemon: daemon, 192 }, 193 } 194 195 name := "u1,u3" 196 h, err := ParseHandle( 197 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, name, 198 tlf.Private) 199 require.NoError(t, err) 200 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 201 assert.Equal(t, tlf.CanonicalName(name), h.GetCanonicalName()) 202 203 // Make sure that generating another handle doesn't change the 204 // name. 205 h2, err := MakeHandle( 206 context.Background(), h.ToBareHandleOrBust(), tlf.Private, 207 kbpki, kbpki, nil, keybase1.OfflineAvailability_NONE) 208 require.NoError(t, err) 209 assert.Equal(t, tlf.CanonicalName(name), h2.GetCanonicalName()) 210 } 211 212 func TestParseHandleAssertionPublicSuccess(t *testing.T) { 213 ctx := context.Background() 214 215 localUsers := idutil.MakeLocalUsers( 216 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 217 currentUID := localUsers[0].UID 218 daemon := idutil.NewDaemonLocal( 219 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 220 221 kbpki := &idutiltest.IdentifyCountingKBPKI{ 222 KBPKI: &idutiltest.DaemonKBPKI{ 223 Daemon: daemon, 224 }, 225 } 226 227 name := "u1,u2,u3" 228 h, err := ParseHandle( 229 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Public)}, nil, 230 name, tlf.Public) 231 require.NoError(t, err) 232 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 233 assert.Equal(t, tlf.CanonicalName(name), h.GetCanonicalName()) 234 235 // Make sure that generating another handle doesn't change the 236 // name. 237 h2, err := MakeHandle( 238 context.Background(), h.ToBareHandleOrBust(), tlf.Public, 239 kbpki, kbpki, nil, keybase1.OfflineAvailability_NONE) 240 require.NoError(t, err) 241 assert.Equal(t, tlf.CanonicalName(name), h2.GetCanonicalName()) 242 } 243 244 func TestHandleAccessorsPrivate(t *testing.T) { 245 ctx := context.Background() 246 247 localUsers := idutil.MakeLocalUsers( 248 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 249 currentUID := localUsers[0].UID 250 daemon := idutil.NewDaemonLocal( 251 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 252 253 kbpki := &idutiltest.DaemonKBPKI{ 254 Daemon: daemon, 255 } 256 257 name := "u1,u2@twitter,u3,u4@twitter#u2,u5@twitter,u6@twitter" 258 h, err := ParseHandle( 259 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 260 name, tlf.Private) 261 require.NoError(t, err) 262 263 require.False(t, h.Type() == tlf.Public) 264 265 require.True(t, h.IsWriter(localUsers[0].UID)) 266 require.True(t, h.IsReader(localUsers[0].UID)) 267 268 require.False(t, h.IsWriter(localUsers[1].UID)) 269 require.True(t, h.IsReader(localUsers[1].UID)) 270 271 require.True(t, h.IsWriter(localUsers[2].UID)) 272 require.True(t, h.IsReader(localUsers[2].UID)) 273 274 for i := 6; i < 10; i++ { 275 u := keybase1.MakeTestUID(uint32(i)) 276 require.False(t, h.IsWriter(u)) 277 require.False(t, h.IsReader(u)) 278 } 279 280 require.Equal(t, h.ResolvedWriters(), 281 []keybase1.UserOrTeamID{ 282 localUsers[0].UID.AsUserOrTeam(), 283 localUsers[2].UID.AsUserOrTeam(), 284 }) 285 require.Equal(t, h.FirstResolvedWriter(), localUsers[0].UID.AsUserOrTeam()) 286 287 require.Equal(t, h.ResolvedReaders(), 288 []keybase1.UserOrTeamID{ 289 localUsers[1].UID.AsUserOrTeam(), 290 }) 291 292 require.Equal(t, h.UnresolvedWriters(), 293 []keybase1.SocialAssertion{ 294 { 295 User: "u2", 296 Service: "twitter", 297 }, 298 { 299 User: "u4", 300 Service: "twitter", 301 }, 302 }) 303 require.Equal(t, h.UnresolvedReaders(), 304 []keybase1.SocialAssertion{ 305 { 306 User: "u5", 307 Service: "twitter", 308 }, 309 { 310 User: "u6", 311 Service: "twitter", 312 }, 313 }) 314 } 315 316 func TestHandleAccessorsPublic(t *testing.T) { 317 ctx := context.Background() 318 319 localUsers := idutil.MakeLocalUsers( 320 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 321 currentUID := localUsers[0].UID 322 daemon := idutil.NewDaemonLocal( 323 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 324 325 kbpki := &idutiltest.DaemonKBPKI{ 326 Daemon: daemon, 327 } 328 329 name := "u1,u2@twitter,u3,u4@twitter" 330 h, err := ParseHandle( 331 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Public)}, nil, 332 name, tlf.Public) 333 require.NoError(t, err) 334 335 require.True(t, h.Type() == tlf.Public) 336 337 require.True(t, h.IsWriter(localUsers[0].UID)) 338 require.True(t, h.IsReader(localUsers[0].UID)) 339 340 require.False(t, h.IsWriter(localUsers[1].UID)) 341 require.True(t, h.IsReader(localUsers[1].UID)) 342 343 require.True(t, h.IsWriter(localUsers[2].UID)) 344 require.True(t, h.IsReader(localUsers[2].UID)) 345 346 for i := 6; i < 10; i++ { 347 u := keybase1.MakeTestUID(uint32(i)) 348 require.False(t, h.IsWriter(u)) 349 require.True(t, h.IsReader(u)) 350 } 351 352 require.Equal(t, h.ResolvedWriters(), 353 []keybase1.UserOrTeamID{ 354 localUsers[0].UID.AsUserOrTeam(), 355 localUsers[2].UID.AsUserOrTeam(), 356 }) 357 require.Equal(t, h.FirstResolvedWriter(), localUsers[0].UID.AsUserOrTeam()) 358 359 require.Nil(t, h.ResolvedReaders()) 360 361 require.Equal(t, h.UnresolvedWriters(), 362 []keybase1.SocialAssertion{ 363 { 364 User: "u2", 365 Service: "twitter", 366 }, 367 { 368 User: "u4", 369 Service: "twitter", 370 }, 371 }) 372 require.Nil(t, h.UnresolvedReaders()) 373 } 374 375 func TestHandleConflictInfo(t *testing.T) { 376 ctx := context.Background() 377 378 localUsers := idutil.MakeLocalUsers( 379 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 380 currentUID := localUsers[0].UID 381 codec := kbfscodec.NewMsgpack() 382 daemon := idutil.NewDaemonLocal(currentUID, localUsers, nil, codec) 383 kbpki := &idutiltest.DaemonKBPKI{ 384 Daemon: daemon, 385 } 386 387 name := "u1,u2,u3" 388 cname := tlf.CanonicalName(name) 389 h, err := ParseHandle(ctx, kbpki, nil, nil, name, tlf.Public) 390 require.NoError(t, err) 391 392 require.Nil(t, h.ConflictInfo()) 393 394 h, err = h.WithUpdatedConflictInfo(codec, nil) 395 require.NoError(t, err) 396 require.Equal(t, h.GetCanonicalName(), cname) 397 398 info := tlf.HandleExtension{ 399 Date: 100, 400 Number: 50, 401 Type: tlf.HandleExtensionConflict, 402 } 403 h, err = h.WithUpdatedConflictInfo(codec, &info) 404 require.NoError(t, err) 405 require.Equal(t, info, *h.ConflictInfo()) 406 cname2 := tlf.CanonicalName(name + tlf.HandleExtensionSep + info.String()) 407 require.Equal(t, h.GetCanonicalName(), cname2) 408 409 info.Date = 101 410 require.NotEqual(t, info, *h.ConflictInfo()) 411 412 info.Date = 100 413 h, err = h.WithUpdatedConflictInfo(codec, &info) 414 cname3 := tlf.CanonicalName(name + tlf.HandleExtensionSep + info.String()) 415 require.NoError(t, err) 416 require.Equal(t, h.GetCanonicalName(), cname3) 417 418 expectedErr := tlf.HandleExtensionMismatchError{ 419 Expected: *h.ConflictInfo(), 420 Actual: nil, 421 } 422 h, err = h.WithUpdatedConflictInfo(codec, nil) 423 require.Equal(t, expectedErr, err) 424 require.Equal(t, "Folder handle extension mismatch, expected: (conflicted copy 1970-01-01 #50), actual: <nil>", err.Error()) 425 426 expectedErr = tlf.HandleExtensionMismatchError{ 427 Expected: *h.ConflictInfo(), 428 Actual: &info, 429 } 430 info.Date = 101 431 _, err = h.WithUpdatedConflictInfo(codec, &info) 432 require.Equal(t, expectedErr, err) 433 // A strange error message, since the difference doesn't show 434 // up in the strings. Oh, well. 435 require.Equal(t, "Folder handle extension mismatch, expected: (conflicted copy 1970-01-01 #50), actual: (conflicted copy 1970-01-01 #50)", err.Error()) 436 } 437 438 func TestHandleFinalizedInfo(t *testing.T) { 439 ctx := context.Background() 440 441 localUsers := idutil.MakeLocalUsers( 442 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 443 currentUID := localUsers[0].UID 444 codec := kbfscodec.NewMsgpack() 445 daemon := idutil.NewDaemonLocal(currentUID, localUsers, nil, codec) 446 kbpki := &idutiltest.DaemonKBPKI{ 447 Daemon: daemon, 448 } 449 450 name := "u1,u2,u3" 451 cname := tlf.CanonicalName(name) 452 h, err := ParseHandle(ctx, kbpki, nil, nil, name, tlf.Public) 453 require.NoError(t, err) 454 455 require.Nil(t, h.FinalizedInfo()) 456 info := tlf.HandleExtension{ 457 Date: 100, 458 Number: 50, 459 Type: tlf.HandleExtensionFinalized, 460 } 461 462 h.SetFinalizedInfo(&info) 463 require.Equal(t, info, *h.FinalizedInfo()) 464 cname2 := tlf.CanonicalName(name + tlf.HandleExtensionSep + info.String()) 465 require.Equal(t, h.GetCanonicalName(), cname2) 466 467 info.Date = 101 468 require.NotEqual(t, info, *h.FinalizedInfo()) 469 470 h.SetFinalizedInfo(nil) 471 require.Nil(t, h.FinalizedInfo()) 472 require.Equal(t, h.GetCanonicalName(), cname) 473 } 474 475 func TestHandleConflictAndFinalizedInfo(t *testing.T) { 476 ctx := context.Background() 477 478 localUsers := idutil.MakeLocalUsers( 479 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 480 currentUID := localUsers[0].UID 481 codec := kbfscodec.NewMsgpack() 482 daemon := idutil.NewDaemonLocal(currentUID, localUsers, nil, codec) 483 kbpki := &idutiltest.DaemonKBPKI{ 484 Daemon: daemon, 485 } 486 487 name := "u1,u2,u3" 488 h, err := ParseHandle(ctx, kbpki, nil, nil, name, tlf.Public) 489 require.NoError(t, err) 490 491 require.Nil(t, h.ConflictInfo()) 492 493 cInfo := tlf.HandleExtension{ 494 Date: 100, 495 Number: 50, 496 Type: tlf.HandleExtensionConflict, 497 } 498 h, err = h.WithUpdatedConflictInfo(codec, &cInfo) 499 require.NoError(t, err) 500 require.Equal(t, cInfo, *h.ConflictInfo()) 501 cname2 := tlf.CanonicalName(name + tlf.HandleExtensionSep + cInfo.String()) 502 require.Equal(t, h.GetCanonicalName(), cname2) 503 504 fInfo := tlf.HandleExtension{ 505 Date: 101, 506 Number: 51, 507 Type: tlf.HandleExtensionFinalized, 508 } 509 h.SetFinalizedInfo(&fInfo) 510 require.Equal(t, fInfo, *h.FinalizedInfo()) 511 require.Equal(t, cInfo, *h.ConflictInfo()) 512 cname3 := cname2 + tlf.CanonicalName(tlf.HandleExtensionSep+fInfo.String()) 513 require.Equal(t, h.GetCanonicalName(), cname3) 514 } 515 516 func TestTlfHandlEqual(t *testing.T) { 517 ctx := context.Background() 518 519 localUsers := idutil.MakeLocalUsers( 520 []kbname.NormalizedUsername{"u1", "u2", "u3", "u4", "u5"}) 521 currentUID := localUsers[0].UID 522 codec := kbfscodec.NewMsgpack() 523 daemon := idutil.NewDaemonLocal(currentUID, localUsers, nil, codec) 524 525 kbpki := &idutiltest.DaemonKBPKI{ 526 Daemon: daemon, 527 } 528 529 name1 := "u1,u2@twitter,u3,u4@twitter" 530 h1, err := ParseHandle(ctx, kbpki, nil, nil, name1, tlf.Public) 531 require.NoError(t, err) 532 533 eq, err := h1.Equals(codec, *h1) 534 require.NoError(t, err) 535 require.True(t, eq) 536 537 // Test public bit. 538 539 h2, err := ParseHandle(ctx, kbpki, nil, nil, name1, tlf.Private) 540 require.NoError(t, err) 541 eq, err = h1.Equals(codec, *h2) 542 require.NoError(t, err) 543 require.False(t, eq) 544 545 // Test resolved and unresolved readers and writers. 546 547 name1 = "u1,u2@twitter#u3,u4@twitter" 548 h1, err = ParseHandle(ctx, kbpki, nil, nil, name1, tlf.Private) 549 require.NoError(t, err) 550 551 for _, name2 := range []string{ 552 "u1,u2@twitter,u5#u3,u4@twitter", 553 "u1,u5@twitter#u3,u4@twitter", 554 "u1,u2@twitter#u4@twitter,u5", 555 "u1,u2@twitter#u3,u5@twitter", 556 } { 557 h2, err := ParseHandle(ctx, kbpki, nil, nil, name2, tlf.Private) 558 require.NoError(t, err) 559 eq, err := h1.Equals(codec, *h2) 560 require.NoError(t, err) 561 require.False(t, eq) 562 } 563 564 // Test conflict info and finalized info. 565 566 h2, err = ParseHandle(ctx, kbpki, nil, nil, name1, tlf.Private) 567 require.NoError(t, err) 568 info := tlf.HandleExtension{ 569 Date: 100, 570 Number: 50, 571 Type: tlf.HandleExtensionConflict, 572 } 573 h2, err = h2.WithUpdatedConflictInfo(codec, &info) 574 require.NoError(t, err) 575 576 eq, err = h1.Equals(codec, *h2) 577 require.NoError(t, err) 578 require.False(t, eq) 579 580 h2, err = ParseHandle(ctx, kbpki, nil, nil, name1, tlf.Private) 581 require.NoError(t, err) 582 h2.SetFinalizedInfo(&info) 583 584 eq, err = h1.Equals(codec, *h2) 585 require.NoError(t, err) 586 require.False(t, eq) 587 588 // Test failure on name difference. 589 h2, err = ParseHandle(ctx, kbpki, nil, nil, name1, tlf.Private) 590 require.NoError(t, err) 591 h2.name += "x" 592 eq, err = h1.Equals(codec, *h2) 593 require.NoError(t, err) 594 require.False(t, eq) 595 } 596 597 func TestParseHandleSocialAssertion(t *testing.T) { 598 ctx := context.Background() 599 600 localUsers := idutil.MakeLocalUsers( 601 []kbname.NormalizedUsername{"u1", "u2"}) 602 currentUID := localUsers[0].UID 603 daemon := idutil.NewDaemonLocal( 604 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 605 606 kbpki := &idutiltest.IdentifyCountingKBPKI{ 607 KBPKI: &idutiltest.DaemonKBPKI{ 608 Daemon: daemon, 609 }, 610 } 611 612 name := "u1,u2#u3@twitter" 613 h, err := ParseHandle( 614 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 615 name, tlf.Private) 616 assert.Equal(t, 0, kbpki.GetIdentifyCalls()) 617 require.NoError(t, err) 618 assert.Equal(t, tlf.CanonicalName(name), h.GetCanonicalName()) 619 620 // Make sure that generating another handle doesn't change the 621 // name. 622 h2, err := MakeHandle( 623 context.Background(), h.ToBareHandleOrBust(), tlf.Private, 624 kbpki, kbpki, nil, keybase1.OfflineAvailability_NONE) 625 require.NoError(t, err) 626 assert.Equal(t, tlf.CanonicalName(name), h2.GetCanonicalName()) 627 } 628 629 func TestParseHandleUIDAssertion(t *testing.T) { 630 ctx := context.Background() 631 632 localUsers := idutil.MakeLocalUsers( 633 []kbname.NormalizedUsername{"u1", "u2"}) 634 currentUID := localUsers[0].UID 635 daemon := idutil.NewDaemonLocal( 636 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 637 638 kbpki := &idutiltest.IdentifyCountingKBPKI{ 639 KBPKI: &idutiltest.DaemonKBPKI{ 640 Daemon: daemon, 641 }, 642 } 643 644 a := currentUID.String() + "@uid" 645 _, err := ParseHandle( 646 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 647 a, tlf.Private) 648 assert.Equal(t, 1, kbpki.GetIdentifyCalls()) 649 assert.Equal(t, idutil.TlfNameNotCanonical{ 650 Name: a, NameToTry: "u1"}, errors.Cause(err)) 651 } 652 653 func TestParseHandleAndAssertion(t *testing.T) { 654 ctx := context.Background() 655 656 localUsers := idutil.MakeLocalUsers( 657 []kbname.NormalizedUsername{"u1", "u2"}) 658 localUsers[0].Asserts = []string{"u1@twitter"} 659 currentUID := localUsers[0].UID 660 daemon := idutil.NewDaemonLocal( 661 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 662 663 kbpki := &idutiltest.IdentifyCountingKBPKI{ 664 KBPKI: &idutiltest.DaemonKBPKI{ 665 Daemon: daemon, 666 }, 667 } 668 669 a := currentUID.String() + "@uid+u1@twitter" 670 _, err := ParseHandle( 671 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 672 a, tlf.Private) 673 // We expect 1 extra identify for compound assertions until 674 // KBFS-2022 is completed. 675 assert.Equal(t, 1+1, kbpki.GetIdentifyCalls()) 676 assert.Equal(t, idutil.TlfNameNotCanonical{ 677 Name: a, NameToTry: "u1"}, errors.Cause(err)) 678 } 679 680 func TestParseHandleConflictSuffix(t *testing.T) { 681 ctx := context.Background() 682 683 localUsers := idutil.MakeLocalUsers([]kbname.NormalizedUsername{"u1"}) 684 currentUID := localUsers[0].UID 685 daemon := idutil.NewDaemonLocal( 686 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 687 688 kbpki := &idutiltest.DaemonKBPKI{ 689 Daemon: daemon, 690 } 691 692 ci := &tlf.HandleExtension{ 693 Date: 1462838400, 694 Number: 1, 695 Type: tlf.HandleExtensionConflict, 696 } 697 698 a := "u1 " + ci.String() 699 h, err := ParseHandle( 700 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 701 a, tlf.Private) 702 require.NoError(t, err) 703 require.NotNil(t, h.ConflictInfo()) 704 require.Equal(t, ci.String(), h.ConflictInfo().String()) 705 } 706 707 func TestParseHandleFailConflictingAssertion(t *testing.T) { 708 ctx := context.Background() 709 710 localUsers := idutil.MakeLocalUsers( 711 []kbname.NormalizedUsername{"u1", "u2"}) 712 localUsers[1].Asserts = []string{"u2@twitter"} 713 currentUID := localUsers[0].UID 714 daemon := idutil.NewDaemonLocal( 715 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 716 717 kbpki := &idutiltest.IdentifyCountingKBPKI{ 718 KBPKI: &idutiltest.DaemonKBPKI{ 719 Daemon: daemon, 720 }, 721 } 722 723 a := currentUID.String() + "@uid+u2@twitter" 724 _, err := ParseHandle( 725 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 726 a, tlf.Private) 727 // We expect 1 extra identify for compound assertions until 728 // KBFS-2022 is completed. 729 assert.Equal(t, 0+1, kbpki.GetIdentifyCalls()) 730 require.Error(t, err) 731 } 732 733 func TestResolveAgainBasic(t *testing.T) { 734 ctx := context.Background() 735 736 localUsers := idutil.MakeLocalUsers( 737 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 738 currentUID := localUsers[0].UID 739 daemon := idutil.NewDaemonLocal( 740 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 741 742 kbpki := &idutiltest.DaemonKBPKI{ 743 Daemon: daemon, 744 } 745 746 name := "u1,u2#u3@twitter" 747 h, err := ParseHandle( 748 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 749 name, tlf.Private) 750 require.NoError(t, err) 751 assert.Equal(t, tlf.CanonicalName(name), h.GetCanonicalName()) 752 753 // ResolveAgain shouldn't rely on resolving the original names again. 754 daemon.AddNewAssertionForTestOrBust("u3", "u3@twitter") 755 newH, err := h.ResolveAgain(ctx, kbpki, nil, nil) 756 require.NoError(t, err) 757 assert.Equal(t, tlf.CanonicalName("u1,u2#u3"), newH.GetCanonicalName()) 758 } 759 760 func TestResolveAgainDoubleAsserts(t *testing.T) { 761 ctx := context.Background() 762 763 localUsers := idutil.MakeLocalUsers( 764 []kbname.NormalizedUsername{"u1", "u2"}) 765 currentUID := localUsers[0].UID 766 daemon := idutil.NewDaemonLocal( 767 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 768 769 kbpki := &idutiltest.DaemonKBPKI{ 770 Daemon: daemon, 771 } 772 773 name := "u1,u1@github,u1@twitter#u2,u2@github,u2@twitter" 774 h, err := ParseHandle( 775 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 776 name, tlf.Private) 777 require.NoError(t, err) 778 assert.Equal(t, tlf.CanonicalName(name), h.GetCanonicalName()) 779 780 daemon.AddNewAssertionForTestOrBust("u1", "u1@twitter") 781 daemon.AddNewAssertionForTestOrBust("u1", "u1@github") 782 daemon.AddNewAssertionForTestOrBust("u2", "u2@twitter") 783 daemon.AddNewAssertionForTestOrBust("u2", "u2@github") 784 newH, err := h.ResolveAgain(ctx, kbpki, nil, nil) 785 require.NoError(t, err) 786 assert.Equal(t, tlf.CanonicalName("u1#u2"), newH.GetCanonicalName()) 787 } 788 789 func TestResolveAgainWriterReader(t *testing.T) { 790 ctx := context.Background() 791 792 localUsers := idutil.MakeLocalUsers( 793 []kbname.NormalizedUsername{"u1", "u2"}) 794 currentUID := localUsers[0].UID 795 daemon := idutil.NewDaemonLocal( 796 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 797 798 kbpki := &idutiltest.DaemonKBPKI{ 799 Daemon: daemon, 800 } 801 802 name := "u1,u2@github#u2@twitter" 803 h, err := ParseHandle( 804 ctx, kbpki, ConstIDGetter{tlf.FakeID(1, tlf.Private)}, nil, 805 name, tlf.Private) 806 require.NoError(t, err) 807 assert.Equal(t, tlf.CanonicalName(name), h.GetCanonicalName()) 808 809 daemon.AddNewAssertionForTestOrBust("u2", "u2@twitter") 810 daemon.AddNewAssertionForTestOrBust("u2", "u2@github") 811 newH, err := h.ResolveAgain(ctx, kbpki, nil, nil) 812 require.NoError(t, err) 813 assert.Equal(t, tlf.CanonicalName("u1,u2"), newH.GetCanonicalName()) 814 } 815 816 func TestResolveAgainConflict(t *testing.T) { 817 ctx := context.Background() 818 819 localUsers := idutil.MakeLocalUsers( 820 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 821 currentUID := localUsers[0].UID 822 daemon := idutil.NewDaemonLocal( 823 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 824 825 kbpki := &idutiltest.DaemonKBPKI{ 826 Daemon: daemon, 827 } 828 829 name := "u1,u2#u3@twitter" 830 id := tlf.FakeID(1, tlf.Private) 831 h, err := ParseHandle( 832 ctx, kbpki, ConstIDGetter{id}, nil, name, tlf.Private) 833 require.NoError(t, err) 834 assert.Equal(t, tlf.CanonicalName(name), h.GetCanonicalName()) 835 836 daemon.AddNewAssertionForTestOrBust("u3", "u3@twitter") 837 ext, err := tlf.NewHandleExtension(tlf.HandleExtensionConflict, 1, "", time.Now()) 838 if err != nil { 839 t.Fatal(err) 840 } 841 h.conflictInfo = ext 842 newH, err := h.ResolveAgain(ctx, kbpki, nil, nil) 843 require.NoError(t, err) 844 assert.Equal(t, tlf.CanonicalName("u1,u2#u3"+ 845 tlf.HandleExtensionSep+ext.String()), newH.GetCanonicalName()) 846 } 847 848 func TestHandleResolvesTo(t *testing.T) { 849 ctx := context.Background() 850 851 localUsers := idutil.MakeLocalUsers( 852 []kbname.NormalizedUsername{"u1", "u2", "u3", "u4", "u5"}) 853 currentUID := localUsers[0].UID 854 codec := kbfscodec.NewMsgpack() 855 daemon := idutil.NewDaemonLocal(currentUID, localUsers, nil, codec) 856 857 kbpki := &idutiltest.DaemonKBPKI{ 858 Daemon: daemon, 859 } 860 861 name1 := "u1,u2@twitter,u3,u4@twitter" 862 idPub := tlf.FakeID(1, tlf.Public) 863 h1, err := ParseHandle( 864 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 865 require.NoError(t, err) 866 867 resolvesTo, partialResolvedH1, err := 868 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h1) 869 require.NoError(t, err) 870 require.True(t, resolvesTo) 871 require.Equal(t, h1, partialResolvedH1) 872 873 // Test different public bit. 874 875 id := tlf.FakeID(1, tlf.Private) 876 h2, err := ParseHandle( 877 ctx, kbpki, ConstIDGetter{id}, nil, name1, tlf.Private) 878 require.NoError(t, err) 879 880 resolvesTo, partialResolvedH1, err = 881 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h2) 882 require.NoError(t, err) 883 require.False(t, resolvesTo) 884 require.Equal(t, h1, partialResolvedH1) 885 886 // Test adding conflict info or finalized info. 887 888 h2, err = ParseHandle( 889 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 890 require.NoError(t, err) 891 info := tlf.HandleExtension{ 892 Date: 100, 893 Number: 50, 894 Type: tlf.HandleExtensionConflict, 895 } 896 h2, err = h2.WithUpdatedConflictInfo(codec, &info) 897 require.NoError(t, err) 898 899 resolvesTo, partialResolvedH1, err = 900 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h2) 901 require.NoError(t, err) 902 require.True(t, resolvesTo) 903 require.Equal(t, h1, partialResolvedH1) 904 905 h2, err = ParseHandle( 906 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 907 require.NoError(t, err) 908 info = tlf.HandleExtension{ 909 Date: 101, 910 Number: 51, 911 Type: tlf.HandleExtensionFinalized, 912 } 913 h2.SetFinalizedInfo(&info) 914 915 resolvesTo, partialResolvedH1, err = 916 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h2) 917 require.NoError(t, err) 918 require.True(t, resolvesTo) 919 require.Equal(t, h1, partialResolvedH1) 920 921 // Test differing conflict info or finalized info. 922 923 h2, err = ParseHandle( 924 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 925 require.NoError(t, err) 926 info = tlf.HandleExtension{ 927 Date: 100, 928 Number: 50, 929 Type: tlf.HandleExtensionConflict, 930 } 931 h2, err = h2.WithUpdatedConflictInfo(codec, &info) 932 require.NoError(t, err) 933 info = tlf.HandleExtension{ 934 Date: 99, 935 Number: 49, 936 Type: tlf.HandleExtensionConflict, 937 } 938 h1, err = h1.WithUpdatedConflictInfo(codec, &info) 939 require.NoError(t, err) 940 941 resolvesTo, _, err = 942 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h2) 943 require.NoError(t, err) 944 require.False(t, resolvesTo) 945 946 h1, err = ParseHandle( 947 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 948 require.NoError(t, err) 949 h2, err = ParseHandle( 950 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 951 require.NoError(t, err) 952 info = tlf.HandleExtension{ 953 Date: 101, 954 Number: 51, 955 Type: tlf.HandleExtensionFinalized, 956 } 957 h2.SetFinalizedInfo(&info) 958 info = tlf.HandleExtension{ 959 Date: 102, 960 Number: 52, 961 Type: tlf.HandleExtensionFinalized, 962 } 963 h1.SetFinalizedInfo(&info) 964 965 resolvesTo, _, err = 966 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h2) 967 require.NoError(t, err) 968 require.False(t, resolvesTo) 969 970 // Try to add conflict info to a finalized handle. 971 972 h2, err = ParseHandle( 973 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 974 require.NoError(t, err) 975 info = tlf.HandleExtension{ 976 Date: 100, 977 Number: 50, 978 Type: tlf.HandleExtensionConflict, 979 } 980 h2, err = h2.WithUpdatedConflictInfo(codec, &info) 981 require.NoError(t, err) 982 983 _, _, err = 984 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h2) 985 require.Error(t, err) 986 987 // Test positive resolution cases. 988 989 name1 = "u1,u2@twitter,u5#u3,u4@twitter" 990 h1, err = ParseHandle( 991 ctx, kbpki, ConstIDGetter{id}, nil, name1, tlf.Private) 992 require.NoError(t, err) 993 994 type testCase struct { 995 name2 string 996 resolveTo string 997 } 998 999 for _, tc := range []testCase{ 1000 // Resolve to new user. 1001 {"u1,u2,u5#u3,u4@twitter", "u2"}, 1002 // Resolve to existing writer. 1003 {"u1,u5#u3,u4@twitter", "u1"}, 1004 // Resolve to existing reader. 1005 {"u1,u3,u5#u4@twitter", "u3"}, 1006 } { 1007 h2, err = ParseHandle( 1008 ctx, kbpki, ConstIDGetter{id}, nil, tc.name2, tlf.Private) 1009 require.NoError(t, err) 1010 1011 daemon.AddNewAssertionForTestOrBust(tc.resolveTo, "u2@twitter") 1012 1013 resolvesTo, partialResolvedH1, err = 1014 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h2) 1015 require.NoError(t, err) 1016 assert.True(t, resolvesTo, tc.name2) 1017 require.Equal(t, h2, partialResolvedH1, tc.name2) 1018 1019 daemon.RemoveAssertionForTest("u2@twitter") 1020 } 1021 1022 // Test negative resolution cases. 1023 1024 for _, tc := range []testCase{ 1025 {"u1,u5#u3,u4@twitter", "u2"}, 1026 {"u1,u2,u5#u3,u4@twitter", "u1"}, 1027 {"u1,u2,u5#u3,u4@twitter", "u3"}, 1028 } { 1029 h2, err = ParseHandle( 1030 ctx, kbpki, ConstIDGetter{id}, nil, tc.name2, tlf.Private) 1031 require.NoError(t, err) 1032 1033 daemon.AddNewAssertionForTestOrBust(tc.resolveTo, "u2@twitter") 1034 1035 resolvesTo, _, err = 1036 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h2) 1037 require.NoError(t, err) 1038 assert.False(t, resolvesTo, tc.name2) 1039 1040 daemon.RemoveAssertionForTest("u2@twitter") 1041 } 1042 } 1043 1044 func TestHandleMigrationResolvesTo(t *testing.T) { 1045 ctx := context.Background() 1046 1047 localUsers := idutil.MakeLocalUsers( 1048 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 1049 currentUID := localUsers[0].UID 1050 codec := kbfscodec.NewMsgpack() 1051 daemon := idutil.NewDaemonLocal(currentUID, localUsers, nil, codec) 1052 1053 kbpki := &idutiltest.DaemonKBPKI{ 1054 Daemon: daemon, 1055 } 1056 1057 t.Log("Simple private team migration") 1058 id := tlf.FakeID(1, tlf.Private) 1059 // Handle without iteam. 1060 name1 := "u1,u2" 1061 h1, err := ParseHandle( 1062 ctx, kbpki, ConstIDGetter{id}, nil, name1, tlf.Private) 1063 require.NoError(t, err) 1064 1065 makeImplicitHandle := func( 1066 name string, ty tlf.Type, id tlf.ID) *Handle { 1067 wrName, suffix, err := tlf.SplitExtension(name) 1068 require.NoError(t, err) 1069 iteamInfo, err := daemon.ResolveIdentifyImplicitTeam( 1070 ctx, wrName, suffix, ty, true, "", 1071 keybase1.OfflineAvailability_NONE) 1072 require.NoError(t, err) 1073 err = daemon.CreateTeamTLF(ctx, iteamInfo.TID, id) 1074 require.NoError(t, err) 1075 h, err := ParseHandle( 1076 ctx, kbpki, ConstIDGetter{id}, nil, name, ty) 1077 require.NoError(t, err) 1078 require.Equal(t, tlf.TeamKeying, h.TypeForKeying()) 1079 return h 1080 } 1081 h2 := makeImplicitHandle(name1, tlf.Private, id) 1082 1083 resolvesTo, partialResolvedH1, err := 1084 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h2) 1085 require.NoError(t, err) 1086 require.True(t, resolvesTo) 1087 require.Equal(t, h1, partialResolvedH1) 1088 1089 t.Log("Simple public team migration") 1090 idPub := tlf.FakeID(1, tlf.Public) 1091 // Handle without iteam. 1092 h1Pub, err := ParseHandle( 1093 ctx, kbpki, ConstIDGetter{idPub}, nil, name1, tlf.Public) 1094 require.NoError(t, err) 1095 h2Pub := makeImplicitHandle(name1, tlf.Public, idPub) 1096 1097 resolvesTo, partialResolvedH1, err = 1098 h1Pub.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{idPub}, nil, *h2Pub) 1099 require.NoError(t, err) 1100 require.True(t, resolvesTo) 1101 require.Equal(t, h1Pub, partialResolvedH1) 1102 1103 t.Log("Bad migration to team with extra user") 1104 name2 := "u1,u2,u3" 1105 h3 := makeImplicitHandle(name2, tlf.Private, id) 1106 resolvesTo, _, err = 1107 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h3) 1108 require.NoError(t, err) 1109 require.False(t, resolvesTo) 1110 1111 t.Log("Bad migration to team with fewer users") 1112 name3 := "u1" 1113 h4 := makeImplicitHandle(name3, tlf.Private, id) 1114 resolvesTo, _, err = 1115 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h4) 1116 require.NoError(t, err) 1117 require.False(t, resolvesTo) 1118 1119 t.Log("Bad migration to team with new readers") 1120 name4 := "u1,u2#u3" 1121 h5 := makeImplicitHandle(name4, tlf.Private, id) 1122 resolvesTo, _, err = 1123 h1.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h5) 1124 require.NoError(t, err) 1125 require.False(t, resolvesTo) 1126 1127 t.Log("Private team migration with unresolved users") 1128 // Handle without iteam. 1129 name5 := "u1,u2,u3@twitter" 1130 h6, err := ParseHandle( 1131 ctx, kbpki, ConstIDGetter{id}, nil, name5, tlf.Private) 1132 require.NoError(t, err) 1133 h7 := makeImplicitHandle(name5, tlf.Private, id) 1134 resolvesTo, partialResolvedH6, err := 1135 h6.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h7) 1136 require.NoError(t, err) 1137 require.True(t, resolvesTo) 1138 require.Equal(t, h6, partialResolvedH6) 1139 1140 t.Log("Bad private team migration with extra unresolved user") 1141 // Handle without iteam. 1142 name6 := "u1,u2,u3@twitter,u4@twitter" 1143 h8 := makeImplicitHandle(name6, tlf.Private, id) 1144 resolvesTo, _, err = 1145 h6.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h8) 1146 require.NoError(t, err) 1147 require.False(t, resolvesTo) 1148 1149 t.Log("Private team migration with newly-resolved user") 1150 daemon.AddNewAssertionForTestOrBust("u3", "u3@twitter") 1151 resolvesTo, partialResolvedH6, err = 1152 h6.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h3) 1153 require.NoError(t, err) 1154 require.True(t, resolvesTo) 1155 require.Len(t, partialResolvedH6.UnresolvedWriters(), 0) 1156 1157 t.Log("Private team migration with conflict info") 1158 name7 := "u1,u2 (conflicted copy 2016-03-14 #3)" 1159 h9, err := ParseHandle( 1160 ctx, kbpki, ConstIDGetter{id}, nil, name7, tlf.Private) 1161 require.NoError(t, err) 1162 h10 := makeImplicitHandle(name7, tlf.Private, id) 1163 resolvesTo, partialResolvedH9, err := 1164 h9.ResolvesTo(ctx, codec, kbpki, ConstIDGetter{id}, nil, *h10) 1165 require.NoError(t, err) 1166 require.True(t, resolvesTo) 1167 require.Equal(t, h9, partialResolvedH9) 1168 } 1169 1170 func TestParseHandleNoncanonicalExtensions(t *testing.T) { 1171 ctx := context.Background() 1172 1173 localUsers := idutil.MakeLocalUsers( 1174 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 1175 currentUID := localUsers[0].UID 1176 daemon := idutil.NewDaemonLocal( 1177 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 1178 1179 kbpki := &idutiltest.DaemonKBPKI{ 1180 Daemon: daemon, 1181 } 1182 1183 name := "u1,u2#u3 (conflicted copy 2016-03-14 #3) (files before u2 account reset 2016-03-14 #2)" 1184 id := tlf.FakeID(1, tlf.Private) 1185 h, err := ParseHandle( 1186 ctx, kbpki, ConstIDGetter{id}, nil, name, tlf.Private) 1187 require.Nil(t, err) 1188 assert.Equal(t, tlf.HandleExtension{ 1189 Type: tlf.HandleExtensionConflict, 1190 Date: tlf.HandleExtensionStaticTestDate, 1191 Number: 3, 1192 }, *h.ConflictInfo()) 1193 assert.Equal(t, tlf.HandleExtension{ 1194 Type: tlf.HandleExtensionFinalized, 1195 Date: tlf.HandleExtensionStaticTestDate, 1196 Number: 2, 1197 Username: "u2", 1198 }, *h.FinalizedInfo()) 1199 1200 nonCanonicalName := "u1,u2#u3 (files before u2 account reset 2016-03-14 #2) (conflicted copy 2016-03-14 #3)" 1201 _, err = ParseHandle( 1202 ctx, kbpki, ConstIDGetter{id}, nil, nonCanonicalName, tlf.Private) 1203 assert.Equal( 1204 t, idutil.TlfNameNotCanonical{ 1205 Name: nonCanonicalName, NameToTry: name}, errors.Cause(err)) 1206 } 1207 1208 func TestParseHandleImplicitTeams(t *testing.T) { 1209 ctx := context.Background() 1210 1211 localUsers := idutil.MakeLocalUsers( 1212 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 1213 currentUID := localUsers[0].UID 1214 daemon := idutil.NewDaemonLocal( 1215 currentUID, localUsers, nil, kbfscodec.NewMsgpack()) 1216 1217 kbpki := &idutiltest.DaemonKBPKI{ 1218 Daemon: daemon, 1219 } 1220 1221 counter := byte(1) 1222 newITeam := func(name, suffix string, ty tlf.Type) ( 1223 keybase1.TeamID, tlf.ID) { 1224 iteamInfo, err := daemon.ResolveIdentifyImplicitTeam( 1225 ctx, name, suffix, ty, true, "", keybase1.OfflineAvailability_NONE) 1226 require.NoError(t, err) 1227 tlfID := tlf.FakeID(counter, ty) 1228 counter++ 1229 err = daemon.CreateTeamTLF(ctx, iteamInfo.TID, tlfID) 1230 require.NoError(t, err) 1231 return iteamInfo.TID, tlfID 1232 } 1233 1234 check := func(name string, tid keybase1.TeamID, tlfID tlf.ID, ty tlf.Type) { 1235 h, err := ParseHandle(ctx, kbpki, nil, nil, name, ty) 1236 require.NoError(t, err) 1237 require.Len(t, h.ResolvedWriters(), 1) 1238 require.Len(t, h.ResolvedReaders(), 0) 1239 require.Len(t, h.UnresolvedWriters(), 0) 1240 require.Len(t, h.UnresolvedReaders(), 0) 1241 require.Equal(t, tid.String(), h.FirstResolvedWriter().String()) 1242 require.Equal(t, tlfID, h.tlfID) 1243 } 1244 1245 t.Log("Private implicit teams") 1246 tid1, tlfID1 := newITeam("u1", "", tlf.Private) 1247 check("u1", tid1, tlfID1, tlf.Private) 1248 tid2, tlfID2 := newITeam("u1,u2", "", tlf.Private) 1249 check("u1,u2", tid2, tlfID2, tlf.Private) 1250 tid3, tlfID3 := newITeam("u1,u2,u3", "", tlf.Private) 1251 check("u1,u2,u3", tid3, tlfID3, tlf.Private) 1252 1253 t.Log("Public implicit teams") 1254 tid4, tlfID4 := newITeam("u1", "", tlf.Public) 1255 check("u1", tid4, tlfID4, tlf.Public) 1256 tid5, tlfID5 := newITeam("u1,u2", "", tlf.Public) 1257 check("u1,u2", tid5, tlfID5, tlf.Public) 1258 1259 t.Log("Implicit team with a suffix") 1260 tid6, tlfID6 := newITeam( 1261 "u1,u2", "(conflicted copy 2016-03-14 #3)", tlf.Private) 1262 check("u1,u2 (conflicted copy 2016-03-14 #3)", tid6, tlfID6, tlf.Private) 1263 1264 t.Log("Implicit team with readers") 1265 tid7, tlfID7 := newITeam("u1,u2#u3", "", tlf.Private) 1266 check("u1,u2#u3", tid7, tlfID7, tlf.Private) 1267 } 1268 1269 type offlineResolveCounterKBPKI struct { 1270 idutil.KBPKI 1271 1272 lock sync.Mutex 1273 bestEffortOfflineCounts map[string]int 1274 } 1275 1276 func (d *offlineResolveCounterKBPKI) countBestEffort( 1277 offline keybase1.OfflineAvailability, s string) { 1278 if offline != keybase1.OfflineAvailability_BEST_EFFORT { 1279 return 1280 } 1281 d.lock.Lock() 1282 d.bestEffortOfflineCounts[s]++ 1283 d.lock.Unlock() 1284 } 1285 1286 func (d *offlineResolveCounterKBPKI) Resolve( 1287 ctx context.Context, assertion string, 1288 offline keybase1.OfflineAvailability) ( 1289 kbname.NormalizedUsername, keybase1.UserOrTeamID, error) { 1290 d.countBestEffort(offline, assertion) 1291 return d.KBPKI.Resolve(ctx, assertion, offline) 1292 } 1293 1294 func (d *offlineResolveCounterKBPKI) ResolveTeamTLFID( 1295 ctx context.Context, teamID keybase1.TeamID, 1296 offline keybase1.OfflineAvailability) (tlf.ID, error) { 1297 d.countBestEffort(offline, teamID.String()) 1298 return d.KBPKI.ResolveTeamTLFID(ctx, teamID, offline) 1299 } 1300 1301 func (d *offlineResolveCounterKBPKI) ResolveImplicitTeam( 1302 ctx context.Context, assertions, suffix string, tlfType tlf.Type, 1303 offline keybase1.OfflineAvailability) (idutil.ImplicitTeamInfo, error) { 1304 d.countBestEffort(offline, "iteam:"+assertions+" "+suffix) 1305 return d.KBPKI.ResolveImplicitTeam( 1306 ctx, assertions, suffix, tlfType, offline) 1307 } 1308 1309 type testOfflineStatusPathsGetter struct { 1310 bestEffortPaths map[string]bool 1311 } 1312 1313 func (t *testOfflineStatusPathsGetter) OfflineAvailabilityForPath( 1314 tlfPath string) keybase1.OfflineAvailability { 1315 if t.bestEffortPaths[tlfPath] { 1316 return keybase1.OfflineAvailability_BEST_EFFORT 1317 } 1318 return keybase1.OfflineAvailability_NONE 1319 } 1320 1321 func (t *testOfflineStatusPathsGetter) OfflineAvailabilityForID( 1322 tlfID tlf.ID) keybase1.OfflineAvailability { 1323 panic("Not supported") 1324 } 1325 1326 func TestParseHandleOfflineAvailability(t *testing.T) { 1327 ctx := context.Background() 1328 1329 localUsers := idutil.MakeLocalUsers( 1330 []kbname.NormalizedUsername{"u1", "u2", "u3"}) 1331 localUsers[0].Asserts = []string{"u1@twitter"} 1332 currentUID := localUsers[0].UID 1333 localTeams := idutil.MakeLocalTeams( 1334 []kbname.NormalizedUsername{"u1u2u3", "u3u2u1"}) 1335 daemon := idutil.NewDaemonLocal( 1336 currentUID, localUsers, localTeams, kbfscodec.NewMsgpack()) 1337 1338 kbpki := &offlineResolveCounterKBPKI{ 1339 KBPKI: &idutiltest.DaemonKBPKI{ 1340 Daemon: daemon, 1341 }, 1342 bestEffortOfflineCounts: make(map[string]int), 1343 } 1344 1345 osg := &testOfflineStatusPathsGetter{make(map[string]bool)} 1346 osg.bestEffortPaths["/keybase/private/u2"] = true 1347 1348 t.Log("Check unsynced private TLF") 1349 _, err := ParseHandle(ctx, kbpki, nil, osg, "u1", tlf.Private) 1350 require.NoError(t, err) 1351 require.Equal(t, kbpki.bestEffortOfflineCounts["u1"], 0) 1352 1353 t.Log("Check synced private TLF") 1354 _, err = ParseHandle(ctx, kbpki, nil, osg, "u2", tlf.Private) 1355 require.NoError(t, err) 1356 require.Equal(t, kbpki.bestEffortOfflineCounts["u2"], 1) 1357 1358 t.Log("Check synced private shared TLF") 1359 osg.bestEffortPaths["/keybase/private/u1,u2,u3"] = true 1360 _, err = ParseHandle(ctx, kbpki, nil, osg, "u1,u2,u3", tlf.Private) 1361 require.NoError(t, err) 1362 require.Equal(t, 1, kbpki.bestEffortOfflineCounts["u1"]) 1363 require.Equal(t, 2, kbpki.bestEffortOfflineCounts["u2"]) 1364 require.Equal(t, 1, kbpki.bestEffortOfflineCounts["u3"]) 1365 1366 t.Log("Check synced private shared TLF, different order") 1367 _, err = ParseHandle(ctx, kbpki, nil, osg, "u3,u1,u2", tlf.Private) 1368 assert.Equal( 1369 t, idutil.TlfNameNotCanonical{Name: "u3,u1,u2", NameToTry: "u1,u2,u3"}, 1370 errors.Cause(err)) 1371 require.Equal(t, 2, kbpki.bestEffortOfflineCounts["u1"]) 1372 require.Equal(t, 3, kbpki.bestEffortOfflineCounts["u2"]) 1373 require.Equal(t, 2, kbpki.bestEffortOfflineCounts["u3"]) 1374 1375 t.Log("Check synced private shared TLF, " + 1376 "resolved assertions don't use best effort.") 1377 _, err = ParseHandle(ctx, kbpki, nil, osg, "u1@twitter,u2,u3", 1378 tlf.Private) 1379 assert.Equal( 1380 t, idutil.TlfNameNotCanonical{ 1381 Name: "u1@twitter,u2,u3", NameToTry: "u1,u2,u3"}, 1382 errors.Cause(err)) 1383 require.Equal(t, 2, kbpki.bestEffortOfflineCounts["u1"]) 1384 require.Equal(t, 0, kbpki.bestEffortOfflineCounts["u1@twitter"]) 1385 require.Equal(t, 3, kbpki.bestEffortOfflineCounts["u2"]) 1386 require.Equal(t, 2, kbpki.bestEffortOfflineCounts["u3"]) 1387 1388 t.Log("Check synced private shared TLF, " + 1389 "unresolved assertions do use best effort.") 1390 osg.bestEffortPaths["/keybase/private/u1,u2@twitter,u3"] = true 1391 _, err = ParseHandle(ctx, kbpki, nil, osg, "u1,u2@twitter,u3", 1392 tlf.Private) 1393 assert.NoError(t, err) 1394 require.Equal(t, 3, kbpki.bestEffortOfflineCounts["u1"]) 1395 require.Equal(t, 1, kbpki.bestEffortOfflineCounts["u2@twitter"]) 1396 require.Equal(t, 3, kbpki.bestEffortOfflineCounts["u2"]) 1397 require.Equal(t, 3, kbpki.bestEffortOfflineCounts["u3"]) 1398 1399 t.Log("Check synced private shared TLF, with readers") 1400 osg.bestEffortPaths["/keybase/private/u1#u2,u3"] = true 1401 _, err = ParseHandle(ctx, kbpki, nil, osg, "u1#u2,u3", 1402 tlf.Private) 1403 assert.NoError(t, err) 1404 require.Equal(t, 4, kbpki.bestEffortOfflineCounts["u1"]) 1405 require.Equal(t, 4, kbpki.bestEffortOfflineCounts["u2"]) 1406 require.Equal(t, 4, kbpki.bestEffortOfflineCounts["u3"]) 1407 1408 t.Log("Check synced private shared TLF, with readers, different order") 1409 _, err = ParseHandle(ctx, kbpki, nil, osg, "u1#u3,u2", 1410 tlf.Private) 1411 assert.Equal( 1412 t, idutil.TlfNameNotCanonical{Name: "u1#u3,u2", NameToTry: "u1#u2,u3"}, 1413 errors.Cause(err)) 1414 require.Equal(t, 5, kbpki.bestEffortOfflineCounts["u1"]) 1415 require.Equal(t, 5, kbpki.bestEffortOfflineCounts["u2"]) 1416 require.Equal(t, 5, kbpki.bestEffortOfflineCounts["u3"]) 1417 1418 t.Log("Check synced private shared TLF, with extension") 1419 ext := "(conflicted copy 2016-03-14 #3)" 1420 osg.bestEffortPaths["/keybase/private/u1,u2 "+ext] = true 1421 _, err = ParseHandle( 1422 ctx, kbpki, nil, osg, "u1,u2 "+ext, tlf.Private) 1423 assert.NoError(t, err) 1424 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u1"]) 1425 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u2"]) 1426 require.Equal(t, 5, kbpki.bestEffortOfflineCounts["u3"]) 1427 1428 t.Log("Check synced private shared TLF, with extension, different order, " + 1429 "with reader and unresolved assertion") 1430 osg.bestEffortPaths["/keybase/private/u1,u3#u2@twitter "+ext] = true 1431 _, err = ParseHandle( 1432 ctx, kbpki, nil, osg, "u3,u1#u2@twitter "+ext, tlf.Private) 1433 assert.Equal( 1434 t, idutil.TlfNameNotCanonical{ 1435 Name: "u3,u1#u2@twitter " + ext, 1436 NameToTry: "u1,u3#u2@twitter " + ext, 1437 }, errors.Cause(err)) 1438 require.Equal(t, 7, kbpki.bestEffortOfflineCounts["u1"]) 1439 require.Equal(t, 2, kbpki.bestEffortOfflineCounts["u2@twitter"]) 1440 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u2"]) 1441 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u3"]) 1442 1443 t.Log("Check synced team TLF") 1444 osg.bestEffortPaths["/keybase/team/u1u2u3"] = true 1445 tlfID1 := tlf.FakeID(1, tlf.SingleTeam) 1446 err = daemon.CreateTeamTLF(ctx, localTeams[0].TID, tlfID1) 1447 require.NoError(t, err) 1448 _, err = ParseHandle( 1449 ctx, kbpki, ConstIDGetter{tlfID1}, osg, "u1u2u3", tlf.SingleTeam) 1450 assert.NoError(t, err) 1451 require.Equal(t, 1, kbpki.bestEffortOfflineCounts["team:u1u2u3"]) 1452 require.Equal(t, 7, kbpki.bestEffortOfflineCounts["u1"]) 1453 require.Equal(t, 2, kbpki.bestEffortOfflineCounts["u2@twitter"]) 1454 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u2"]) 1455 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u3"]) 1456 require.Equal( 1457 t, 1, kbpki.bestEffortOfflineCounts[localTeams[0].TID.String()]) 1458 1459 t.Log("Check unsynced team TLF") 1460 tlfID2 := tlf.FakeID(2, tlf.SingleTeam) 1461 err = daemon.CreateTeamTLF(ctx, localTeams[1].TID, tlfID2) 1462 require.NoError(t, err) 1463 _, err = ParseHandle( 1464 ctx, kbpki, ConstIDGetter{tlfID2}, osg, "u3u2u1", tlf.SingleTeam) 1465 assert.NoError(t, err) 1466 require.Equal(t, 1, kbpki.bestEffortOfflineCounts["team:u1u2u3"]) 1467 require.Equal(t, 0, kbpki.bestEffortOfflineCounts["team:u3u2u1"]) 1468 1469 t.Log("Check implicit team TLF") 1470 info, err := daemon.ResolveIdentifyImplicitTeam( 1471 ctx, "u1,u2,u3", "", tlf.Private, true, "", 1472 keybase1.OfflineAvailability_BEST_EFFORT) 1473 require.NoError(t, err) 1474 tlfID3 := tlf.FakeID(3, tlf.Private) 1475 err = daemon.CreateTeamTLF(ctx, info.TID, tlfID3) 1476 require.NoError(t, err) 1477 _, err = ParseHandle(ctx, kbpki, nil, osg, "u1,u2,u3", tlf.Private) 1478 require.NoError(t, err) 1479 // The iteam has a best-effort count of 3, because the earlier 1480 // lookup of 'u1,u2,u3' and 'u3,u1,u2' already tried to find an 1481 // implicit team once with best-effort, but there wasn't yet a TLF 1482 // ID associated with the implicit team. The per-user counts 1483 // won't change now that the existence of the TLF ID for the iteam 1484 // short-circuits those lookups. 1485 require.Equal(t, 3, kbpki.bestEffortOfflineCounts["iteam:u1,u2,u3 "]) 1486 require.Equal(t, 7, kbpki.bestEffortOfflineCounts["u1"]) 1487 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u2"]) 1488 require.Equal(t, 6, kbpki.bestEffortOfflineCounts["u3"]) 1489 }