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  }