github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/teams/rotate_test.go (about) 1 package teams 2 3 import ( 4 "context" 5 "encoding/hex" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/keybase/client/go/kbtest" 11 "github.com/keybase/client/go/libkb" 12 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestRotate(t *testing.T) { 17 tc, owner, other, _, name := memberSetupMultiple(t) 18 defer tc.Cleanup() 19 20 if err := SetRoleWriter(context.TODO(), tc.G, name, other.Username); err != nil { 21 t.Fatal(err) 22 } 23 24 team, err := GetForTestByStringName(context.TODO(), tc.G, name) 25 if err != nil { 26 t.Fatal(err) 27 } 28 if team.Generation() != 1 { 29 t.Fatalf("initial team generation: %d, expected 1", team.Generation()) 30 } 31 secretBefore := team.Data.PerTeamKeySeedsUnverified[team.Generation()].Seed.ToBytes() 32 keys1, err := team.AllApplicationKeys(context.TODO(), keybase1.TeamApplication_CHAT) 33 if err != nil { 34 t.Fatal(err) 35 } 36 require.Equal(t, len(keys1), 1) 37 require.Equal(t, keys1[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1)) 38 39 if err := team.Rotate(context.TODO(), keybase1.RotationType_VISIBLE); err != nil { 40 t.Fatal(err) 41 } 42 43 after, err := GetForTestByStringName(context.TODO(), tc.G, name) 44 if err != nil { 45 t.Fatal(err) 46 } 47 if after.Generation() != 2 { 48 t.Fatalf("rotated team generation: %d, expected 2", after.Generation()) 49 } 50 secretAfter := after.Data.PerTeamKeySeedsUnverified[after.Generation()].Seed.ToBytes() 51 if libkb.SecureByteArrayEq(secretAfter, secretBefore) { 52 t.Fatal("TeamBox.Ctext did not change when rotated") 53 } 54 55 assertRole(tc, name, owner.Username, keybase1.TeamRole_OWNER) 56 assertRole(tc, name, other.Username, keybase1.TeamRole_WRITER) 57 58 keys2, err := after.AllApplicationKeys(context.TODO(), keybase1.TeamApplication_CHAT) 59 require.NoError(t, err) 60 require.Equal(t, len(keys2), 2) 61 require.Equal(t, keys2[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1)) 62 require.Equal(t, keys1[0].Key, keys2[0].Key) 63 } 64 65 func TestRotateWithBots(t *testing.T) { 66 tc, owner, otherA, otherB, name := memberSetupMultiple(t) 67 defer tc.Cleanup() 68 69 err := SetRoleBot(context.TODO(), tc.G, name, otherA.Username) 70 require.NoError(t, err) 71 72 err = SetRoleRestrictedBot(context.TODO(), tc.G, name, otherB.Username, keybase1.TeamBotSettings{}) 73 require.NoError(t, err) 74 75 err = tc.Logout() 76 require.NoError(t, err) 77 require.NoError(t, otherA.Login(tc.G)) 78 team, err := GetForTestByStringName(context.TODO(), tc.G, name) 79 require.NoError(t, err) 80 require.EqualValues(t, 1, team.Generation()) 81 require.Len(t, team.Data.PerTeamKeySeedsUnverified, 1) 82 _, err = team.AllApplicationKeys(context.TODO(), keybase1.TeamApplication_CHAT) 83 require.NoError(t, err) 84 85 // Regular bots cannot rotate 86 err = team.Rotate(context.TODO(), keybase1.RotationType_VISIBLE) 87 require.Error(t, err) 88 89 err = tc.Logout() 90 require.NoError(t, err) 91 require.NoError(t, otherB.Login(tc.G)) 92 team, err = GetForTestByStringName(context.TODO(), tc.G, name) 93 require.NoError(t, err) 94 require.EqualValues(t, 1, team.Generation()) 95 require.Zero(t, len(team.Data.PerTeamKeySeedsUnverified)) 96 _, err = team.AllApplicationKeys(context.TODO(), keybase1.TeamApplication_CHAT) 97 require.Error(t, err) 98 require.IsType(t, libkb.NotFoundError{}, err) 99 100 // Restricted bots cannot rotate 101 err = team.Rotate(context.TODO(), keybase1.RotationType_VISIBLE) 102 require.IsType(t, libkb.NotFoundError{}, err) 103 104 err = tc.Logout() 105 require.NoError(t, err) 106 require.NoError(t, owner.Login(tc.G)) 107 team, err = GetForTestByStringName(context.TODO(), tc.G, name) 108 require.NoError(t, err) 109 err = team.Rotate(context.TODO(), keybase1.RotationType_VISIBLE) 110 require.NoError(t, err) 111 112 // otherA has 2 seeds 113 err = tc.Logout() 114 require.NoError(t, err) 115 require.NoError(t, otherA.Login(tc.G)) 116 after, err := GetForTestByStringName(context.TODO(), tc.G, name) 117 require.NoError(t, err) 118 require.EqualValues(t, 2, after.Generation()) 119 require.Len(t, after.Data.PerTeamKeySeedsUnverified, 2) 120 121 // otherB has none 122 err = tc.Logout() 123 require.NoError(t, err) 124 require.NoError(t, otherB.Login(tc.G)) 125 after, err = GetForTestByStringName(context.TODO(), tc.G, name) 126 require.NoError(t, err) 127 require.EqualValues(t, 2, after.Generation()) 128 require.Zero(t, len(after.Data.PerTeamKeySeedsUnverified)) 129 _, err = after.AllApplicationKeys(context.TODO(), keybase1.TeamApplication_CHAT) 130 require.Error(t, err) 131 require.IsType(t, libkb.NotFoundError{}, err) 132 133 assertRole(tc, name, owner.Username, keybase1.TeamRole_OWNER) 134 assertRole(tc, name, otherA.Username, keybase1.TeamRole_BOT) 135 assertRole(tc, name, otherB.Username, keybase1.TeamRole_RESTRICTEDBOT) 136 } 137 138 func setupRotateTest(t *testing.T, implicit bool, public bool) (tc libkb.TestContext, owner, other *kbtest.FakeUser, teamID keybase1.TeamID, teamName keybase1.TeamName) { 139 tc = SetupTest(t, "team", 1) 140 141 var usernames []string 142 143 other, err := kbtest.CreateAndSignupFakeUser("team", tc.G) 144 require.NoError(t, err) 145 usernames = append(usernames, other.Username) 146 err = tc.Logout() 147 require.NoError(t, err) 148 149 owner, err = kbtest.CreateAndSignupFakeUser("team", tc.G) 150 require.NoError(t, err) 151 usernames = append(usernames, owner.Username) 152 153 if implicit { 154 t.Logf("creating implicit team") 155 displayName := strings.Join(usernames, ",") 156 var team *Team 157 team, teamName, _, err = LookupOrCreateImplicitTeam(context.TODO(), tc.G, displayName, public) 158 require.NoError(t, err) 159 160 return tc, owner, other, team.ID, teamName 161 } 162 if public { 163 t.Fatalf("public teams not supported") 164 } 165 166 t.Logf("creating team") 167 teamName, teamID = createTeam2(tc) 168 169 t.Logf("adding writer") 170 err = SetRoleWriter(context.TODO(), tc.G, teamName.String(), other.Username) 171 require.NoError(t, err) 172 173 return tc, owner, other, teamID, teamName 174 } 175 176 func TestHandleRotateRequestOldGeneration(t *testing.T) { 177 runMany(t, func(implicit, public bool) { 178 tc, owner, other, teamID, _ := setupRotateTest(t, implicit, public) 179 defer tc.Cleanup() 180 181 team, err := GetForTestByID(context.TODO(), tc.G, teamID) 182 require.NoError(t, err) 183 184 // rotate to bump the generation 185 err = team.Rotate(context.TODO(), keybase1.RotationType_VISIBLE) 186 require.NoError(t, err) 187 188 team, err = GetForTestByID(context.TODO(), tc.G, teamID) 189 require.NoError(t, err) 190 if team.Generation() != 2 { 191 t.Fatalf("team generation: %d, expected 2", team.Generation()) 192 } 193 secretBefore := team.Data.PerTeamKeySeedsUnverified[team.Generation()].Seed.ToBytes() 194 195 // this shouldn't do anything 196 err = HandleRotateRequest(context.TODO(), tc.G, keybase1.TeamCLKRMsg{ 197 TeamID: team.ID, 198 Generation: 1, 199 ResetUsersUntrusted: nil, 200 }) 201 require.NoError(t, err) 202 203 after, err := GetForTestByID(context.TODO(), tc.G, teamID) 204 require.NoError(t, err) 205 if after.Generation() != 2 { 206 t.Fatalf("HandleRotateRequest with old generation changed team generation: %d, expected 2", after.Generation()) 207 } 208 secretAfter := after.Data.PerTeamKeySeedsUnverified[after.Generation()].Seed.ToBytes() 209 require.True(t, libkb.SecureByteArrayEq(secretAfter, secretBefore), "team secret changed after HandleRotateRequest with old generation") 210 211 if implicit { 212 assertRole2(tc, teamID, owner.Username, keybase1.TeamRole_OWNER) 213 assertRole2(tc, teamID, other.Username, keybase1.TeamRole_OWNER) 214 } else { 215 assertRole2(tc, teamID, owner.Username, keybase1.TeamRole_OWNER) 216 assertRole2(tc, teamID, other.Username, keybase1.TeamRole_WRITER) 217 } 218 }) 219 } 220 221 func TestHandleRotateRequest(t *testing.T) { 222 runMany(t, func(implicit, public bool) { 223 tc, owner, other, teamID, _ := setupRotateTest(t, implicit, public) 224 defer tc.Cleanup() 225 226 team, err := GetForTestByID(context.TODO(), tc.G, teamID) 227 require.NoError(t, err) 228 if team.Generation() != 1 { 229 t.Fatalf("initial team generation: %d, expected 1", team.Generation()) 230 } 231 secretBefore := team.Data.PerTeamKeySeedsUnverified[team.Generation()].Seed.ToBytes() 232 233 err = HandleRotateRequest(context.TODO(), tc.G, keybase1.TeamCLKRMsg{ 234 TeamID: team.ID, 235 Generation: team.Generation(), 236 ResetUsersUntrusted: nil, 237 }) 238 require.NoError(t, err) 239 240 after, err := GetForTestByID(context.TODO(), tc.G, teamID) 241 require.NoError(t, err) 242 if after.Generation() != 2 { 243 t.Fatalf("rotated team generation: %d, expected 2", after.Generation()) 244 } 245 secretAfter := after.Data.PerTeamKeySeedsUnverified[after.Generation()].Seed.ToBytes() 246 require.False(t, libkb.SecureByteArrayEq(secretAfter, secretBefore), "team secret should change when rotated") 247 248 if implicit { 249 assertRole2(tc, teamID, owner.Username, keybase1.TeamRole_OWNER) 250 assertRole2(tc, teamID, other.Username, keybase1.TeamRole_OWNER) 251 } else { 252 assertRole2(tc, teamID, owner.Username, keybase1.TeamRole_OWNER) 253 assertRole2(tc, teamID, other.Username, keybase1.TeamRole_WRITER) 254 } 255 }) 256 } 257 258 func TestImplicitAdminAfterRotateRequest(t *testing.T) { 259 tc, owner, otherA, otherB, root, sub := memberSetupSubteam(t) 260 defer tc.Cleanup() 261 262 team, err := GetForTestByStringName(context.TODO(), tc.G, sub) 263 require.NoError(t, err) 264 require.EqualValues(t, 1, team.Generation()) 265 secretBefore := team.Data.PerTeamKeySeedsUnverified[team.Generation()].Seed.ToBytes() 266 267 params := keybase1.TeamCLKRMsg{ 268 TeamID: team.ID, 269 Generation: team.Generation(), 270 ResetUsersUntrusted: nil, 271 } 272 require.NoError(t, HandleRotateRequest(context.TODO(), tc.G, params)) 273 274 after, err := GetForTestByStringName(context.TODO(), tc.G, sub) 275 require.NoError(t, err) 276 277 require.EqualValues(t, 2, after.Generation()) 278 secretAfter := after.Data.PerTeamKeySeedsUnverified[after.Generation()].Seed.ToBytes() 279 require.False(t, libkb.SecureByteArrayEq(secretAfter, secretBefore)) 280 281 // make sure the roles are ok after rotate 282 assertRole(tc, root, owner.Username, keybase1.TeamRole_OWNER) 283 assertRole(tc, root, otherA.Username, keybase1.TeamRole_ADMIN) 284 assertRole(tc, root, otherB.Username, keybase1.TeamRole_NONE) 285 assertRole(tc, sub, owner.Username, keybase1.TeamRole_NONE) 286 assertRole(tc, sub, otherA.Username, keybase1.TeamRole_NONE) 287 assertRole(tc, sub, otherB.Username, keybase1.TeamRole_NONE) 288 289 // otherA (an implicit admin of sub) should be able to add otherB to sub 290 // after the rotate 291 292 // switch to `otherA` user 293 err = tc.Logout() 294 require.NoError(t, err) 295 require.NoError(t, otherA.Login(tc.G)) 296 297 // otherA has the power to add otherB to the subteam 298 res, err := AddMember(context.TODO(), tc.G, sub, otherB.Username, keybase1.TeamRole_WRITER, nil) 299 require.NoError(t, err) 300 require.Equal(t, res.User.Username, otherB.Username) 301 // otherB should now be a writer 302 assertRole(tc, sub, otherB.Username, keybase1.TeamRole_WRITER) 303 304 // owner, otherA should still be non-members 305 assertRole(tc, sub, owner.Username, keybase1.TeamRole_NONE) 306 assertRole(tc, sub, otherA.Username, keybase1.TeamRole_NONE) 307 } 308 309 // Test multiple rotations racing to post chain links to the same team. 310 // The expected behavior is that they each either succeed or run out of attempts. 311 func TestRotateRace(t *testing.T) { 312 _, tcs, cleanup := setupNTests(t, 1) 313 defer cleanup() 314 315 t.Logf("U0 creates A") 316 _, rootID := createTeam2(*tcs[0]) 317 318 rotate := func(userIndexOperator int) <-chan error { 319 errCh := make(chan error) 320 go func() { 321 params := keybase1.TeamCLKRMsg{ 322 TeamID: rootID, 323 Generation: keybase1.PerTeamKeyGeneration(100), 324 ResetUsersUntrusted: nil, 325 } 326 err := HandleRotateRequest(context.TODO(), tcs[userIndexOperator].G, params) 327 errCh <- err 328 }() 329 return errCh 330 } 331 332 assertNoErr := func(errCh <-chan error, msgAndArgs ...interface{}) { 333 select { 334 case err := <-errCh: 335 require.NoError(t, err, msgAndArgs...) 336 case <-time.After(20 * time.Second): 337 require.FailNow(t, "timeout waiting for return channel") 338 } 339 } 340 341 for i := 0; i < 10; i++ { 342 t.Logf("round %v", i) 343 344 errCh1 := rotate(0) 345 errCh2 := rotate(0) 346 assertNoErr(errCh1, "round %v", i) 347 assertNoErr(errCh2, "round %v", i) 348 } 349 } 350 351 func testRotateTeamSweeping(t *testing.T, open bool) { 352 tc, owner, otherA, otherB, name := memberSetupMultiple(t) 353 defer tc.Cleanup() 354 355 otherC, err := kbtest.CreateAndSignupFakeUser("team", tc.G) 356 require.NoError(t, err) 357 err = tc.Logout() 358 require.NoError(t, err) 359 360 t.Logf("Created team %q", name) 361 require.NoError(t, owner.Login(tc.G)) 362 363 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherA.Username)) 364 require.NoError(t, SetRoleAdmin(context.Background(), tc.G, name, otherB.Username)) 365 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherC.Username)) 366 367 if open { 368 err = ChangeTeamSettings(context.Background(), tc.G, name, keybase1.TeamSettings{ 369 Open: true, 370 JoinAs: keybase1.TeamRole_READER, 371 }) 372 require.NoError(t, err) 373 } 374 375 team, err := GetForTestByStringName(context.Background(), tc.G, name) 376 require.NoError(t, err) 377 378 allMembers, err := team.UsersWithRoleOrAbove(keybase1.TeamRole_READER) 379 require.NoError(t, err) 380 require.Len(t, allMembers, 4) 381 382 // Rotate and reload team while members are not reset yet. Member 383 // set should not change. 384 err = HandleRotateRequest(context.Background(), tc.G, keybase1.TeamCLKRMsg{ 385 TeamID: team.ID, 386 Generation: team.Generation(), 387 ResetUsersUntrusted: nil, 388 }) 389 require.NoError(t, err) 390 team, err = GetForTestByStringName(context.Background(), tc.G, name) 391 require.NoError(t, err) 392 393 members, err := team.Members() 394 require.NoError(t, err) 395 require.Len(t, members.AllUIDs(), 4) 396 397 // Reset otherA (writer) and otherB (admin). otherA should be 398 // removed if the team is open. 399 for _, u := range []*kbtest.FakeUser{otherA, otherB} { 400 err := tc.Logout() 401 require.NoError(t, err) 402 require.NoError(t, u.Login(tc.G)) 403 404 kbtest.ResetAccount(tc, u) 405 } 406 407 err = tc.Logout() 408 require.NoError(t, err) 409 err = owner.Login(tc.G) 410 require.NoError(t, err) 411 412 // Rotate - should trigger sweeping path if the team is open. 413 params := keybase1.TeamCLKRMsg{ 414 TeamID: team.ID, 415 Generation: team.Generation(), 416 } 417 if open { 418 // If the team is not open, team_rekeyd will not tell us about 419 // reset people. 420 params.ResetUsersUntrusted = []keybase1.TeamCLKRResetUser{ 421 { 422 Uid: otherA.User.GetUID(), 423 UserEldestSeqno: keybase1.Seqno(0), 424 MemberEldestSeqno: keybase1.Seqno(1), 425 }} 426 } 427 err = HandleRotateRequest(context.Background(), tc.G, params) 428 require.NoError(t, err) 429 430 // Reload team and check results. 431 team, err = GetForTestByStringName(context.Background(), tc.G, name) 432 require.NoError(t, err) 433 434 members2, err := team.Members() 435 require.NoError(t, err) 436 if open { 437 allUids := members2.AllUIDs() 438 require.Len(t, allUids, 3) 439 440 require.Contains(t, allUids, owner.User.GetUID()) 441 require.Contains(t, allUids, otherB.User.GetUID()) 442 require.Contains(t, allUids, otherC.User.GetUID()) 443 444 require.NotContains(t, allUids, otherA.User.GetUID()) 445 } else { 446 require.ElementsMatch(t, members2.AllUserVersions(), members.AllUserVersions()) 447 } 448 449 require.Equal(t, keybase1.PerTeamKeyGeneration(3), team.Generation()) 450 } 451 452 func TestRotateTeamSweeping(t *testing.T) { 453 // Tests that when a key rotation is requested, reset members are 454 // removed from open team but not closed team. 455 testRotateTeamSweeping(t, false /* open */) 456 testRotateTeamSweeping(t, true /* open */) 457 } 458 459 func TestRotateWithBadUIDs(t *testing.T) { 460 // Try the rotate key + remove reset members machinery, but 461 // simulate server giving us one bad UID (for a person that has 462 // not reset at all), and UID of an admin, who has reset. Neither 463 // of the users should be removed from the team. 464 465 tc, owner, otherA, otherB, name := memberSetupMultiple(t) 466 defer tc.Cleanup() 467 468 t.Logf("Created team %q", name) 469 470 err := ChangeTeamSettings(context.Background(), tc.G, name, keybase1.TeamSettings{ 471 Open: true, 472 JoinAs: keybase1.TeamRole_WRITER, 473 }) 474 require.NoError(t, err) 475 476 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherA.Username)) 477 require.NoError(t, SetRoleAdmin(context.Background(), tc.G, name, otherB.Username)) 478 479 // Logout and reset (admin member). 480 err = tc.Logout() 481 require.NoError(t, err) 482 require.NoError(t, otherB.Login(tc.G)) 483 kbtest.ResetAccount(tc, otherB) 484 485 // Re-login as owner, simulate CLKR message. 486 err = tc.Logout() 487 require.NoError(t, err) 488 err = owner.Login(tc.G) 489 require.NoError(t, err) 490 491 team, err := GetForTestByStringName(context.Background(), tc.G, name) 492 require.NoError(t, err) 493 494 params := keybase1.TeamCLKRMsg{ 495 TeamID: team.ID, 496 Generation: team.Generation(), 497 } 498 for _, u := range []*kbtest.FakeUser{otherA, otherB} { 499 // otherA has not reset at all, but assume it ended up in the 500 // message. otherB has really reset, but is an admin. 501 params.ResetUsersUntrusted = append(params.ResetUsersUntrusted, 502 keybase1.TeamCLKRResetUser{ 503 Uid: u.User.GetUID(), 504 UserEldestSeqno: keybase1.Seqno(0), 505 MemberEldestSeqno: keybase1.Seqno(1), 506 }) 507 } 508 509 err = HandleRotateRequest(context.Background(), tc.G, params) 510 require.NoError(t, err) 511 512 // Check that no one has been removed, and team generation has 513 // changed. 514 team, err = GetForTestByStringName(context.Background(), tc.G, name) 515 require.NoError(t, err) 516 517 members, err := team.Members() 518 require.NoError(t, err) 519 require.Len(t, members.AllUserVersions(), 3) 520 allUids := members.AllUIDs() 521 require.Contains(t, allUids, owner.User.GetUID()) 522 require.Contains(t, allUids, otherA.User.GetUID()) 523 require.Contains(t, allUids, otherB.User.GetUID()) 524 525 require.Equal(t, keybase1.PerTeamKeyGeneration(2), team.Generation()) 526 } 527 528 func TestRotateResetMultipleUsers(t *testing.T) { 529 // Same reset test but with multiple users being removed at once. 530 tc, owner, otherA, otherB, name := memberSetupMultiple(t) 531 defer tc.Cleanup() 532 533 otherC, err := kbtest.CreateAndSignupFakeUser("team", tc.G) 534 require.NoError(t, err) 535 err = tc.Logout() 536 require.NoError(t, err) 537 require.NoError(t, owner.Login(tc.G)) 538 539 err = ChangeTeamSettings(context.Background(), tc.G, name, keybase1.TeamSettings{ 540 Open: true, 541 JoinAs: keybase1.TeamRole_WRITER, 542 }) 543 require.NoError(t, err) 544 545 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherA.Username)) 546 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherB.Username)) 547 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherC.Username)) 548 549 team, err := GetForTestByStringName(context.Background(), tc.G, name) 550 require.NoError(t, err) 551 552 params := keybase1.TeamCLKRMsg{ 553 TeamID: team.ID, 554 Generation: team.Generation(), 555 } 556 557 for _, u := range []*kbtest.FakeUser{otherA, otherB, otherC} { 558 err := tc.Logout() 559 require.NoError(t, err) 560 require.NoError(t, u.Login(tc.G)) 561 562 if u != otherC { 563 kbtest.ResetAccount(tc, u) 564 } else { 565 kbtest.DeleteAccount(tc, u) 566 } 567 568 params.ResetUsersUntrusted = append(params.ResetUsersUntrusted, 569 keybase1.TeamCLKRResetUser{ 570 Uid: u.User.GetUID(), 571 UserEldestSeqno: keybase1.Seqno(0), 572 MemberEldestSeqno: keybase1.Seqno(1), 573 }) 574 } 575 576 err = tc.Logout() 577 require.NoError(t, err) 578 err = owner.Login(tc.G) 579 require.NoError(t, err) 580 581 err = HandleRotateRequest(context.Background(), tc.G, params) 582 require.NoError(t, err) 583 584 // Check that everyone has been removed and team generation changed. 585 team, err = GetForTestByStringName(context.Background(), tc.G, name) 586 require.NoError(t, err) 587 588 members, err := team.Members() 589 require.NoError(t, err) 590 allUVs := members.AllUserVersions() 591 require.Len(t, allUVs, 1) 592 require.Contains(t, allUVs, keybase1.NewUserVersion(owner.User.GetUID(), owner.EldestSeqno)) 593 } 594 595 func TestRotateResetSweepWithWriter(t *testing.T) { 596 // Scenario where CLKR with ResetUsersUntrusted is sent to a 597 // writer. They can't remove reset people, but they should rotate 598 // anyway. 599 600 tc, _, otherA, otherB, name := memberSetupMultiple(t) 601 defer tc.Cleanup() 602 603 err := ChangeTeamSettings(context.Background(), tc.G, name, keybase1.TeamSettings{ 604 Open: true, 605 JoinAs: keybase1.TeamRole_WRITER, 606 }) 607 require.NoError(t, err) 608 609 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherA.Username)) 610 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherB.Username)) 611 612 // Login as otherB, reset account. 613 err = tc.Logout() 614 require.NoError(t, err) 615 require.NoError(t, otherB.Login(tc.G)) 616 kbtest.ResetAccount(tc, otherB) 617 618 // Login as otherA (writer), simulate CLKR with info about reset 619 // otherB. 620 err = tc.Logout() 621 require.NoError(t, err) 622 require.NoError(t, otherA.Login(tc.G)) 623 624 team, err := GetForTestByStringName(context.Background(), tc.G, name) 625 require.NoError(t, err) 626 627 params := keybase1.TeamCLKRMsg{ 628 TeamID: team.ID, 629 Generation: team.Generation(), 630 ResetUsersUntrusted: []keybase1.TeamCLKRResetUser{ 631 { 632 Uid: otherB.User.GetUID(), 633 UserEldestSeqno: keybase1.Seqno(0), 634 MemberEldestSeqno: keybase1.Seqno(1), 635 }}, 636 } 637 err = HandleRotateRequest(context.Background(), tc.G, params) 638 require.NoError(t, err) 639 640 team, err = GetForTestByStringName(context.Background(), tc.G, name) 641 require.NoError(t, err) 642 require.EqualValues(t, 2, team.Generation()) 643 } 644 645 func TestRemoveWithoutRotation(t *testing.T) { 646 tc, _, otherA, otherB, name := memberSetupMultiple(t) 647 defer tc.Cleanup() 648 649 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherA.Username)) 650 651 team, err := GetForTestByStringName(context.Background(), tc.G, name) 652 require.NoError(t, err) 653 654 req := keybase1.TeamChangeReq{ 655 Writers: []keybase1.UserVersion{ 656 keybase1.NewUserVersion(otherB.User.GetUID(), otherB.EldestSeqno), 657 }, 658 None: []keybase1.UserVersion{ 659 keybase1.NewUserVersion(otherA.User.GetUID(), otherA.EldestSeqno), 660 }, 661 } 662 663 opts := ChangeMembershipOptions{ 664 SkipKeyRotation: true, 665 } 666 err = team.ChangeMembershipWithOptions(context.Background(), req, opts) 667 require.NoError(t, err) 668 669 require.EqualValues(t, 1, team.Generation()) 670 671 team, err = GetForTestByStringName(context.Background(), tc.G, name) 672 require.NoError(t, err) 673 // Generation should still be one, ChangeMembership should not 674 // have posted new key. 675 require.EqualValues(t, 1, team.Generation()) 676 } 677 678 func TestRotateAsSubteamWriter(t *testing.T) { 679 // subteam has a single writer who is not part of the parent team. 680 // scenario manifested itself in CORE-8681 681 tc, _, _, otherB, _, sub := memberSetupSubteam(t) 682 defer tc.Cleanup() 683 684 team, err := GetForTestByStringName(context.TODO(), tc.G, sub) 685 require.NoError(t, err) 686 oldGeneration := team.Generation() 687 688 res, err := AddMember(context.TODO(), tc.G, sub, otherB.Username, keybase1.TeamRole_WRITER, nil) 689 require.NoError(t, err) 690 require.Equal(t, res.User.Username, otherB.Username) 691 // otherB should now be a writer 692 assertRole(tc, sub, otherB.Username, keybase1.TeamRole_WRITER) 693 694 err = tc.Logout() 695 require.NoError(t, err) 696 require.NoError(t, otherB.Login(tc.G)) 697 698 params := keybase1.TeamCLKRMsg{ 699 TeamID: team.ID, 700 Generation: oldGeneration, 701 ResetUsersUntrusted: nil, 702 } 703 err = HandleRotateRequest(context.Background(), tc.G, params) 704 require.NoError(t, err) 705 706 teamAfter, err := GetForTestByStringName(context.Background(), tc.G, sub) 707 require.NoError(t, err) 708 require.EqualValues(t, oldGeneration+1, teamAfter.Generation()) 709 } 710 711 func TestDowngradeImplicitAdminAfterReset(t *testing.T) { 712 tc, owner, otherA, otherB, root, sub := memberSetupSubteam(t) 713 defer tc.Cleanup() 714 715 _, err := AddMember(context.TODO(), tc.G, sub, otherA.Username, keybase1.TeamRole_ADMIN, nil) 716 require.NoError(t, err) 717 718 // Reset and reprovision implicit admin 719 err = tc.Logout() 720 require.NoError(t, err) 721 require.NoError(t, otherA.Login(tc.G)) 722 kbtest.ResetAccount(tc, otherA) 723 require.NoError(t, otherA.Login(tc.G)) 724 725 err = tc.Logout() 726 require.NoError(t, err) 727 require.NoError(t, owner.Login(tc.G)) 728 729 _, err = GetForTestByStringName(context.Background(), tc.G, root) 730 require.NoError(t, err) 731 732 _, err = AddMember(context.TODO(), tc.G, root, otherA.Username, keybase1.TeamRole_ADMIN, nil) 733 require.NoError(t, err) 734 735 err = EditMember(context.TODO(), tc.G, root, otherA.Username, keybase1.TeamRole_WRITER, nil) 736 require.NoError(t, err) 737 738 // This fails if a box incorrectly remains live for otherA after the downgrade due 739 // to bad team key coverage. 740 _, err = AddMember(context.TODO(), tc.G, sub, otherB.Username, keybase1.TeamRole_ADMIN, nil) 741 require.NoError(t, err) 742 } 743 744 func TestRotationWhenClosingOpenTeam(t *testing.T) { 745 tc := SetupTest(t, "team", 1) 746 defer tc.Cleanup() 747 748 _, err := kbtest.CreateAndSignupFakeUser("team", tc.G) 749 require.NoError(t, err) 750 751 tryCloseTeam := func(rotateWithSettings bool) { 752 t.Logf("tryCloseTeam(rotateWithSettings=%t)", rotateWithSettings) 753 754 b, err := libkb.RandBytes(4) 755 require.NoError(tc.T, err) 756 757 teamName := hex.EncodeToString(b) 758 _, err = CreateRootTeam(context.Background(), tc.G, teamName, keybase1.TeamSettings{ 759 Open: true, 760 JoinAs: keybase1.TeamRole_WRITER, 761 }) 762 require.NoError(tc.T, err) 763 764 teamObj, err := GetForTestByStringName(context.Background(), tc.G, teamName) 765 require.NoError(t, err) 766 767 currentGen := teamObj.Generation() 768 if rotateWithSettings { 769 err = ChangeTeamSettings(context.Background(), tc.G, teamName, keybase1.TeamSettings{ 770 Open: false, 771 }) 772 require.NoError(t, err) 773 } else { 774 err = teamObj.PostTeamSettings(context.Background(), keybase1.TeamSettings{ 775 Open: false, 776 }, false /* rotate */) 777 require.NoError(t, err) 778 err = RotateKey(context.Background(), tc.G, keybase1.TeamRotateKeyArg{ 779 TeamID: teamObj.ID, 780 Rt: keybase1.RotationType_VISIBLE, 781 }) 782 require.NoError(t, err) 783 } 784 785 teamObj, err = GetForTestByStringName(context.Background(), tc.G, teamName) 786 require.NoError(t, err) // ensures team settings link did not break loading 787 require.Equal(t, currentGen+1, teamObj.Generation()) // and we got new per team key 788 if rotateWithSettings { 789 // Make sure we only posted one link to close. 790 require.EqualValues(t, 2, teamObj.CurrentSeqno()) 791 } else { 792 // This one should have posted two links. 793 require.EqualValues(t, 3, teamObj.CurrentSeqno()) 794 } 795 } 796 797 // Close team using ChangeTeamSettings service_helper API, which is the 798 // default used by RPC handler, and closes team by using PostTeamSettings 799 // with rotate=true. This should close team and rotate in one sigchain 800 // link. 801 tryCloseTeam(true) 802 803 // Try to close team "manually" using PostTeamSettings(rotate=false) and 804 // then calling RotateKey. Team will be closed and rotated in two links. 805 // This is what clients used to do prior to 2019-10-02. 806 tryCloseTeam(false) 807 } 808 809 func TestRemoveFromOpenTeam(t *testing.T) { 810 // Removals from open teams should not cause rotation. 811 tc, _, otherA, _, name := memberSetupMultiple(t) 812 defer tc.Cleanup() 813 814 err := ChangeTeamSettings(context.Background(), tc.G, name, keybase1.TeamSettings{ 815 Open: true, 816 JoinAs: keybase1.TeamRole_WRITER, 817 }) 818 require.NoError(t, err) 819 820 teamObj, err := GetForTestByStringName(context.Background(), tc.G, name) 821 require.NoError(t, err) 822 823 currentGen := teamObj.Generation() 824 err = SetRoleWriter(context.Background(), tc.G, name, otherA.Username) 825 require.NoError(t, err) 826 827 err = RemoveMember(context.Background(), tc.G, name, otherA.Username) 828 require.NoError(t, err) 829 830 // Expecting generation to stay the same after removal. 831 teamObj, err = GetForTestByStringName(context.Background(), tc.G, name) 832 require.NoError(t, err) 833 require.Equal(t, currentGen, teamObj.Generation()) 834 } 835 836 func TestOpenSweepHandler(t *testing.T) { 837 tc, owner, otherA, otherB, name := memberSetupMultiple(t) 838 defer tc.Cleanup() 839 840 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherA.Username)) 841 require.NoError(t, SetRoleWriter(context.Background(), tc.G, name, otherB.Username)) 842 843 otherBUV := otherB.User.ToUserVersion() 844 845 // Login as otherB, reset account. 846 err := tc.Logout() 847 require.NoError(t, err) 848 require.NoError(t, otherB.Login(tc.G)) 849 kbtest.ResetAccount(tc, otherB) 850 851 // Login as owner, try to simulate OPENSWEEP, should fail because it's a 852 // closed team. 853 err = tc.Logout() 854 require.NoError(t, err) 855 require.NoError(t, owner.Login(tc.G)) 856 857 team, err := GetForTestByStringName(context.Background(), tc.G, name) 858 require.NoError(t, err) 859 860 // Make sure we have the right UV that we are going to check later if it's 861 // sweeped out. 862 require.True(t, team.IsMember(context.TODO(), otherBUV)) 863 864 params := keybase1.TeamOpenSweepMsg{ 865 TeamID: team.ID, 866 ResetUsersUntrusted: []keybase1.TeamCLKRResetUser{ 867 { 868 Uid: otherB.User.GetUID(), 869 UserEldestSeqno: keybase1.Seqno(0), 870 MemberEldestSeqno: otherBUV.EldestSeqno, 871 }}, 872 } 873 err = HandleOpenTeamSweepRequest(context.Background(), tc.G, params) 874 require.Error(t, err) 875 876 // Change settings to open team. 877 err = ChangeTeamSettings(context.Background(), tc.G, name, keybase1.TeamSettings{ 878 Open: true, 879 JoinAs: keybase1.TeamRole_WRITER, 880 }) 881 require.NoError(t, err) 882 883 // Login as otherA (writer), simulate OPENSWEEP, should fail 884 // because it only works with admins. 885 err = tc.Logout() 886 require.NoError(t, err) 887 require.NoError(t, otherA.Login(tc.G)) 888 889 err = HandleOpenTeamSweepRequest(context.Background(), tc.G, params) 890 require.Error(t, err) 891 892 // Back to owner, should work now. 893 err = tc.Logout() 894 require.NoError(t, err) 895 require.NoError(t, owner.Login(tc.G)) 896 897 err = HandleOpenTeamSweepRequest(context.Background(), tc.G, params) 898 require.NoError(t, err) 899 900 team, err = GetForTestByStringName(context.Background(), tc.G, name) 901 require.NoError(t, err) 902 // Generation should not have advanced after OPENSWEEP. 903 require.EqualValues(t, 1, team.Generation()) 904 // OtherB should not be a member anymore. 905 require.False(t, team.IsMember(context.TODO(), otherBUV)) 906 // This leaves two remaining members. 907 members, err := team.Members() 908 require.NoError(t, err) 909 require.Len(t, members.AllUserVersions(), 2) 910 911 curSeqno := team.CurrentSeqno() 912 913 // Repeating the same request should be a no-op, not post any links, etc. 914 err = HandleOpenTeamSweepRequest(context.Background(), tc.G, params) 915 require.NoError(t, err) 916 917 team, err = GetForTestByStringName(context.Background(), tc.G, name) 918 require.NoError(t, err) 919 require.Equal(t, curSeqno, team.CurrentSeqno()) 920 require.EqualValues(t, 1, team.Generation()) 921 } 922 923 func TestTeamSettings(t *testing.T) { 924 tc, _, _, _, name := memberSetupMultiple(t) 925 defer tc.Cleanup() 926 927 // Change settings to open team. 928 err := ChangeTeamSettings(context.Background(), tc.G, name, keybase1.TeamSettings{ 929 Open: true, 930 JoinAs: keybase1.TeamRole_WRITER, 931 }) 932 require.NoError(t, err) 933 934 team, err := GetForTestByStringName(context.Background(), tc.G, name) 935 require.NoError(t, err) 936 settings := team.Settings() 937 require.Equal(t, settings.Open, true) 938 require.Equal(t, settings.JoinAs, keybase1.TeamRole_WRITER) 939 }