github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/teams/ftl_test.go (about)

     1  package teams
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/keybase/client/go/kbtest"
     7  	"github.com/keybase/client/go/libkb"
     8  	"github.com/keybase/client/go/protocol/keybase1"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestFastLoaderBasic(t *testing.T) {
    13  	tc := SetupTest(t, "team", 1)
    14  	defer tc.Cleanup()
    15  
    16  	_, err := kbtest.CreateAndSignupFakeUser("team", tc.G)
    17  	require.NoError(t, err)
    18  
    19  	t.Logf("create a team")
    20  	teamName, teamID := createTeam2(tc)
    21  
    22  	t.Logf("load the team")
    23  	arg := keybase1.FastTeamLoadArg{
    24  		ID:            teamID,
    25  		Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
    26  		NeedLatestKey: true,
    27  	}
    28  	m := libkb.NewMetaContextForTest(tc)
    29  	team, err := tc.G.GetFastTeamLoader().Load(m, arg)
    30  	require.NoError(t, err)
    31  	require.Equal(t, len(team.ApplicationKeys), 1)
    32  	require.True(t, teamName.Eq(team.Name))
    33  
    34  	t.Logf("load the team again")
    35  	team, err = tc.G.GetFastTeamLoader().Load(m, arg)
    36  	require.NoError(t, err)
    37  	require.Equal(t, len(team.ApplicationKeys), 1)
    38  	require.True(t, teamName.Eq(team.Name))
    39  }
    40  
    41  // Test fast loading a team that does several key rotations.
    42  func TestFastLoaderKeyGen(t *testing.T) {
    43  	fus, tcs, cleanup := setupNTests(t, 4)
    44  	defer cleanup()
    45  
    46  	t.Logf("create team")
    47  	teamName, teamID := createTeam2(*tcs[0])
    48  	m := make([]libkb.MetaContext, 4)
    49  	for i, tc := range tcs {
    50  		m[i] = libkb.NewMetaContextForTest(*tc)
    51  	}
    52  
    53  	t.Logf("add B to the team so they can load it")
    54  	_, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
    55  	require.NoError(t, err)
    56  	t.Logf("add C to the team so they can load it")
    57  	_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[2].Username, keybase1.TeamRole_BOT, nil)
    58  	require.NoError(t, err)
    59  	t.Logf("add D to the team so they can load it")
    60  	_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[3].Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{})
    61  	require.NoError(t, err)
    62  
    63  	t.Logf("B's first load at gen 1")
    64  	arg := keybase1.FastTeamLoadArg{
    65  		ID:            teamID,
    66  		Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
    67  		NeedLatestKey: true,
    68  	}
    69  	team, err := tcs[1].G.GetFastTeamLoader().Load(m[1], arg)
    70  	require.NoError(t, err)
    71  	require.Equal(t, len(team.ApplicationKeys), 1)
    72  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1))
    73  	require.True(t, teamName.Eq(team.Name))
    74  
    75  	t.Logf("C's first load at gen 1")
    76  	arg = keybase1.FastTeamLoadArg{
    77  		ID:            teamID,
    78  		Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
    79  		NeedLatestKey: true,
    80  	}
    81  	team, err = tcs[2].G.GetFastTeamLoader().Load(m[2], arg)
    82  	require.NoError(t, err)
    83  	require.Equal(t, len(team.ApplicationKeys), 1)
    84  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1))
    85  	require.True(t, teamName.Eq(team.Name))
    86  
    87  	t.Logf("D's first load at gen 1")
    88  	arg = keybase1.FastTeamLoadArg{
    89  		ID: teamID,
    90  	}
    91  	team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg)
    92  	require.NoError(t, err)
    93  	// since D is a restricted bot, they should not have access to any keys
    94  	require.Zero(t, len(team.ApplicationKeys))
    95  	require.True(t, teamName.Eq(team.Name))
    96  	arg = keybase1.FastTeamLoadArg{
    97  		ID:            teamID,
    98  		Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
    99  		NeedLatestKey: true,
   100  	}
   101  	team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg)
   102  	require.Error(t, err)
   103  	require.IsType(t, FTLMissingSeedError{}, err)
   104  	require.Zero(t, len(team.ApplicationKeys))
   105  
   106  	t.Logf("rotate the key a bunch of times")
   107  	// Rotate the key by removing and adding B from the team
   108  	for i := 0; i < 3; i++ {
   109  		err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username)
   110  		require.NoError(t, err)
   111  
   112  		_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   113  		require.NoError(t, err)
   114  	}
   115  
   116  	t.Logf("load as A to check the progression")
   117  	team, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   118  	require.NoError(t, err)
   119  	require.Equal(t, len(team.ApplicationKeys), 1)
   120  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4))
   121  	require.True(t, teamName.Eq(team.Name))
   122  
   123  	t.Logf("B loads the new PTK by number")
   124  	arg.NeedLatestKey = false
   125  	arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(4)}
   126  	team, err = tcs[1].G.GetFastTeamLoader().Load(m[1], arg)
   127  	require.NoError(t, err)
   128  	require.Equal(t, len(team.ApplicationKeys), 1)
   129  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4))
   130  	require.True(t, teamName.Eq(team.Name))
   131  
   132  	t.Logf("B loads the new PTK by latest")
   133  	arg.NeedLatestKey = true
   134  	arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{}
   135  	team, err = tcs[1].G.GetFastTeamLoader().Load(m[1], arg)
   136  	require.NoError(t, err)
   137  	require.Equal(t, len(team.ApplicationKeys), 1)
   138  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4))
   139  	require.True(t, teamName.Eq(team.Name))
   140  
   141  	t.Logf("clear A's FTL state")
   142  	ftl, ok := tcs[0].G.GetFastTeamLoader().(*FastTeamChainLoader)
   143  	require.True(t, ok)
   144  	require.NoError(t, ftl.OnLogout(m[0]))
   145  
   146  	t.Logf("more tests as A; let's first load at generation=1")
   147  	arg = keybase1.FastTeamLoadArg{
   148  		ID:                   teamID,
   149  		Applications:         []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   150  		KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(1)},
   151  	}
   152  	team, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   153  	require.NoError(t, err)
   154  	require.Equal(t, len(team.ApplicationKeys), 1)
   155  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1))
   156  	require.True(t, teamName.Eq(team.Name))
   157  
   158  	t.Logf("let's now load at the latest generation")
   159  	arg = keybase1.FastTeamLoadArg{
   160  		ID:            teamID,
   161  		Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   162  		NeedLatestKey: true,
   163  	}
   164  	team, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   165  	require.NoError(t, err)
   166  	require.Equal(t, len(team.ApplicationKeys), 1)
   167  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4))
   168  	require.True(t, teamName.Eq(team.Name))
   169  
   170  	t.Logf("make sure D still doesn't have access")
   171  	arg = keybase1.FastTeamLoadArg{
   172  		ID: teamID,
   173  	}
   174  	team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg)
   175  	require.NoError(t, err)
   176  	require.Zero(t, len(team.ApplicationKeys))
   177  	require.True(t, teamName.Eq(team.Name))
   178  
   179  	arg = keybase1.FastTeamLoadArg{
   180  		ID:                   teamID,
   181  		Applications:         []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   182  		KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(1)},
   183  	}
   184  	team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg)
   185  	require.Error(t, err)
   186  	require.IsType(t, FTLMissingSeedError{}, err)
   187  	require.Zero(t, len(team.ApplicationKeys))
   188  
   189  	t.Logf("upgrade D to a bot and check they have access")
   190  	err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[3].Username)
   191  	require.NoError(t, err)
   192  	_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[3].Username, keybase1.TeamRole_BOT, nil)
   193  	require.NoError(t, err)
   194  
   195  	arg.NeedLatestKey = false
   196  	arg.KeyGenerationsNeeded = []keybase1.PerTeamKeyGeneration{keybase1.PerTeamKeyGeneration(4)}
   197  	team, err = tcs[3].G.GetFastTeamLoader().Load(m[3], arg)
   198  	require.NoError(t, err)
   199  	require.Equal(t, len(team.ApplicationKeys), 1)
   200  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(4))
   201  	require.True(t, teamName.Eq(team.Name))
   202  }
   203  
   204  // Test loading a sub-sub-team: a.b.c.
   205  func TestFastLoaderMultilevel(t *testing.T) {
   206  	fus, tcs, cleanup := setupNTests(t, 2)
   207  	defer cleanup()
   208  
   209  	t.Logf("create a team")
   210  	parentName, _ := createTeam2(*tcs[0])
   211  
   212  	t.Logf("create a subteam (of parent %s)", parentName)
   213  	m := make([]libkb.MetaContext, 2)
   214  	for i, tc := range tcs {
   215  		m[i] = libkb.NewMetaContextForTest(*tc)
   216  	}
   217  	_, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", parentName, keybase1.TeamRole_NONE /* addSelfAs */)
   218  	require.NoError(t, err)
   219  
   220  	subTeamName, err := parentName.Append("abc")
   221  	require.NoError(t, err)
   222  	t.Logf("create a sub-subteam (of parent %s)", subTeamName)
   223  	subsubteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "def", subTeamName, keybase1.TeamRole_NONE /* addSelfAs */)
   224  	require.NoError(t, err)
   225  
   226  	expectedSubsubTeamName, err := subTeamName.Append("def")
   227  	require.NoError(t, err)
   228  	t.Logf("subsubteam is: %s (%s)", expectedSubsubTeamName.String(), *subsubteamID)
   229  
   230  	t.Logf("add the other user to the subsubteam")
   231  	_, err = AddMember(m[0].Ctx(), tcs[0].G, expectedSubsubTeamName.String(), fus[1].Username, keybase1.TeamRole_WRITER, nil)
   232  	require.NoError(t, err)
   233  
   234  	t.Logf("load the subteam")
   235  	arg := keybase1.FastTeamLoadArg{
   236  		ID:            *subsubteamID,
   237  		Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   238  		NeedLatestKey: true,
   239  	}
   240  	team, err := tcs[1].G.GetFastTeamLoader().Load(m[1], arg)
   241  	require.NoError(t, err)
   242  	require.Equal(t, len(team.ApplicationKeys), 1)
   243  	require.Equal(t, team.ApplicationKeys[0].KeyGeneration, keybase1.PerTeamKeyGeneration(1))
   244  	require.True(t, expectedSubsubTeamName.Eq(team.Name))
   245  }
   246  
   247  func TestFastLoaderUpPointerUnstub(t *testing.T) {
   248  	fus, tcs, cleanup := setupNTests(t, 2)
   249  	defer cleanup()
   250  
   251  	// Require that a team is at this key generation
   252  	t.Logf("create team")
   253  	teamName, teamID := createTeam2(*tcs[0])
   254  	t.Logf("add B to the team so they can load it")
   255  	m := make([]libkb.MetaContext, 2)
   256  	for i, tc := range tcs {
   257  		m[i] = libkb.NewMetaContextForTest(*tc)
   258  	}
   259  	_, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   260  	require.NoError(t, err)
   261  	subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_WRITER /* addSelfAs */)
   262  	require.NoError(t, err)
   263  
   264  	expectedSubTeamName, err := teamName.Append("abc")
   265  	require.NoError(t, err)
   266  	t.Logf("subsubteam is: %s (%s)", expectedSubTeamName.String(), *subteamID)
   267  
   268  	t.Logf("rotate the key a bunch of times")
   269  	// Rotate the key by removing and adding B from the team
   270  	for i := 0; i < 3; i++ {
   271  		err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username)
   272  		require.NoError(t, err)
   273  		_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   274  		require.NoError(t, err)
   275  	}
   276  	t.Logf("load the team")
   277  	arg := keybase1.FastTeamLoadArg{
   278  		ID:            teamID,
   279  		Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   280  		NeedLatestKey: true,
   281  	}
   282  	_, err = tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   283  	require.NoError(t, err)
   284  
   285  	loadSubteam := func() {
   286  		t.Logf("load the subteam")
   287  		arg = keybase1.FastTeamLoadArg{
   288  			ID:            *subteamID,
   289  			Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   290  			NeedLatestKey: true,
   291  		}
   292  		team, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   293  		require.NoError(t, err)
   294  		require.True(t, expectedSubTeamName.Eq(team.Name))
   295  	}
   296  
   297  	// Try again via the unstub system
   298  	loadSubteam()
   299  
   300  	// Also check that it works on a fresh load on a clean cache (thought this
   301  	// duplicates what we did in TestFastLoaderMultilevel)
   302  	ftl, ok := tcs[0].G.GetFastTeamLoader().(*FastTeamChainLoader)
   303  	require.True(t, ok)
   304  	require.NoError(t, ftl.OnLogout(m[0]))
   305  	loadSubteam()
   306  }
   307  
   308  // See CORE-8859, there was a bug that showed up when we loaded the subteam first and then
   309  // the parent, since when we loaded the subteam, we were in "subteam reader" mode, and
   310  // due to a previous server bug, didn't get boxes and prevs back when in subteam reader mode.
   311  // Then, when we tried to access a box in the parent, we would fail. Test that it works.
   312  func TestLoadSubteamThenParent(t *testing.T) {
   313  	fus, tcs, cleanup := setupNTests(t, 2)
   314  	defer cleanup()
   315  
   316  	t.Logf("create team")
   317  	teamName, teamID := createTeam2(*tcs[0])
   318  	t.Logf("add B to the team so they can load it")
   319  	m := make([]libkb.MetaContext, 2)
   320  	for i, tc := range tcs {
   321  		m[i] = libkb.NewMetaContextForTest(*tc)
   322  	}
   323  	_, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   324  	require.NoError(t, err)
   325  	subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_WRITER /* addSelfAs */)
   326  	require.NoError(t, err)
   327  
   328  	expectedSubTeamName, err := teamName.Append("abc")
   329  	require.NoError(t, err)
   330  	t.Logf("subsubteam is: %s (%s)", expectedSubTeamName.String(), *subteamID)
   331  
   332  	t.Logf("rotate the parent team a bunch of times")
   333  	// Rotate the key by removing and adding B from the team
   334  	for i := 0; i < 3; i++ {
   335  		err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username)
   336  		require.NoError(t, err)
   337  		_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   338  		require.NoError(t, err)
   339  	}
   340  
   341  	loadSubteam := func() {
   342  		t.Logf("load the subteam")
   343  		arg := keybase1.FastTeamLoadArg{
   344  			ID:            *subteamID,
   345  			Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   346  			NeedLatestKey: true,
   347  		}
   348  		team, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   349  		require.NoError(t, err)
   350  		require.True(t, expectedSubTeamName.Eq(team.Name))
   351  	}
   352  
   353  	loadTeam := func(g keybase1.PerTeamKeyGeneration) {
   354  		t.Logf("load the team")
   355  		arg := keybase1.FastTeamLoadArg{
   356  			ID:                   teamID,
   357  			Applications:         []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   358  			KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{g},
   359  		}
   360  		_, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   361  		require.NoError(t, err)
   362  	}
   363  
   364  	loadSubteam()
   365  	loadTeam(3)
   366  }
   367  
   368  // See CORE-9207, there was a bug with this order of operations: (1) loading foo.bar;
   369  // (2) being let into foo; (3) loading foo.
   370  func TestLoadSubteamThenAllowedInThenParent(t *testing.T) {
   371  	fus, tcs, cleanup := setupNTests(t, 3)
   372  	defer cleanup()
   373  
   374  	t.Logf("create team")
   375  	teamName, teamID := createTeam2(*tcs[0])
   376  	t.Logf("add B to the team so they can load it")
   377  	m := make([]libkb.MetaContext, 3)
   378  	for i, tc := range tcs {
   379  		m[i] = libkb.NewMetaContextForTest(*tc)
   380  	}
   381  
   382  	rotateKey := func(name keybase1.TeamName) {
   383  		_, err := AddMember(m[0].Ctx(), tcs[0].G, name.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   384  		require.NoError(t, err)
   385  		err = RemoveMember(m[0].Ctx(), tcs[0].G, name.String(), fus[1].Username)
   386  		require.NoError(t, err)
   387  	}
   388  
   389  	for i := 0; i < 3; i++ {
   390  		rotateKey(teamName)
   391  	}
   392  
   393  	subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_ADMIN /* addSelfAs */)
   394  	require.NoError(t, err)
   395  	expectedSubTeamName, err := teamName.Append("abc")
   396  	require.NoError(t, err)
   397  	_, err = AddMember(m[0].Ctx(), tcs[0].G, expectedSubTeamName.String(), fus[2].Username, keybase1.TeamRole_WRITER, nil)
   398  	require.NoError(t, err)
   399  
   400  	loadTeam := func(teamID keybase1.TeamID, g keybase1.PerTeamKeyGeneration) {
   401  		t.Logf("load the team")
   402  		arg := keybase1.FastTeamLoadArg{
   403  			ID:                   teamID,
   404  			Applications:         []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   405  			KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{g},
   406  		}
   407  		_, err := tcs[2].G.GetFastTeamLoader().Load(m[2], arg)
   408  		require.NoError(t, err)
   409  	}
   410  
   411  	loadTeam(*subteamID, 1)
   412  	_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[2].Username, keybase1.TeamRole_WRITER, nil)
   413  	require.NoError(t, err)
   414  	loadTeam(teamID, 1)
   415  }
   416  
   417  // See CORE-8894 for what happened here. The flow is: (1) user loads parent team at generation=N;
   418  // (2) there's a key rotation; (3) loads child team and gets the new box and prevs for generation=N+1,
   419  // but no RKMs; (4) loads the RKMs for the most recent generation. Test a fix for this case.
   420  func TestLoadRKMForLatestCORE8894(t *testing.T) {
   421  	fus, tcs, cleanup := setupNTests(t, 2)
   422  	defer cleanup()
   423  
   424  	// Require that a team is at this key generation
   425  	t.Logf("create team")
   426  	teamName, teamID := createTeam2(*tcs[0])
   427  	t.Logf("add B to the team so they can load it")
   428  	m := make([]libkb.MetaContext, 2)
   429  	for i, tc := range tcs {
   430  		m[i] = libkb.NewMetaContextForTest(*tc)
   431  	}
   432  	_, err := AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   433  	require.NoError(t, err)
   434  	subteamID, err := CreateSubteam(m[0].Ctx(), tcs[0].G, "abc", teamName, keybase1.TeamRole_WRITER /* addSelfAs */)
   435  	require.NoError(t, err)
   436  
   437  	expectedSubTeamName, err := teamName.Append("abc")
   438  	require.NoError(t, err)
   439  	t.Logf("subsubteam is: %s (%s)", expectedSubTeamName.String(), *subteamID)
   440  
   441  	loadTeam := func(id keybase1.TeamID, forceRefresh bool) {
   442  		t.Logf("load the team %s", id)
   443  		arg := keybase1.FastTeamLoadArg{
   444  			ID:            id,
   445  			Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
   446  			ForceRefresh:  forceRefresh,
   447  			NeedLatestKey: true,
   448  		}
   449  		_, err := tcs[0].G.GetFastTeamLoader().Load(m[0], arg)
   450  		require.NoError(t, err)
   451  	}
   452  
   453  	loadTeam(teamID, false)
   454  
   455  	// Rotate the key by removing and adding B from the team
   456  	for i := 0; i < 3; i++ {
   457  		err = RemoveMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username)
   458  		require.NoError(t, err)
   459  		_, err = AddMember(m[0].Ctx(), tcs[0].G, teamName.String(), fus[1].Username, keybase1.TeamRole_READER, nil)
   460  		require.NoError(t, err)
   461  	}
   462  	err = RotateKeyVisible(m[0].Ctx(), tcs[0].G, teamID)
   463  	require.NoError(t, err)
   464  
   465  	loadTeam(*subteamID, true)
   466  	loadTeam(teamID, false)
   467  }