github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/systests/teams_test.go (about)

     1  package systests
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"golang.org/x/net/context"
    10  
    11  	"github.com/davecgh/go-spew/spew"
    12  	"github.com/keybase/client/go/client"
    13  	"github.com/keybase/client/go/engine"
    14  	"github.com/keybase/client/go/kbtest"
    15  	"github.com/keybase/client/go/libkb"
    16  	"github.com/keybase/client/go/protocol/keybase1"
    17  	"github.com/keybase/client/go/protocol/stellar1"
    18  	"github.com/keybase/client/go/service"
    19  	"github.com/keybase/client/go/teams"
    20  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    21  	"github.com/stretchr/testify/require"
    22  
    23  	insecureTriplesec "github.com/keybase/go-triplesec-insecure"
    24  )
    25  
    26  func TestTeamCreate(t *testing.T) {
    27  	tt := newTeamTester(t)
    28  	defer tt.cleanup()
    29  
    30  	tt.addUser("onr")
    31  	tt.addUser("wtr")
    32  
    33  	team := tt.users[0].createTeam()
    34  	tt.users[0].addTeamMember(team, tt.users[1].username, keybase1.TeamRole_WRITER)
    35  }
    36  
    37  func TestTeamBustCache(t *testing.T) {
    38  	tt := newTeamTester(t)
    39  	defer tt.cleanup()
    40  
    41  	tt.addUser("onr")
    42  	tt.addUser("adm")
    43  	tt.addUser("wtr")
    44  
    45  	team := tt.users[0].createTeam()
    46  	tt.users[0].addTeamMember(team, tt.users[1].username, keybase1.TeamRole_ADMIN)
    47  
    48  	before, err := GetTeamForTestByStringName(context.TODO(), tt.users[0].tc.G, team)
    49  	require.NoError(t, err)
    50  	beforeSeqno := before.CurrentSeqno()
    51  	tt.users[1].addTeamMember(team, tt.users[2].username, keybase1.TeamRole_WRITER)
    52  
    53  	// Poll for an update, we should get it as soon as gregor tells us to bust our cache.
    54  	pollForTrue(t, tt.users[0].tc.G, func(i int) bool {
    55  		after, err := teams.Load(context.TODO(), tt.users[0].tc.G, keybase1.LoadTeamArg{
    56  			Name:    team,
    57  			StaleOK: true,
    58  		})
    59  		require.NoError(t, err)
    60  		if after.CurrentSeqno() > beforeSeqno {
    61  			t.Logf("Found new seqno %d at poll loop iter %d", after.CurrentSeqno(), i)
    62  			return true
    63  		}
    64  		return false
    65  	})
    66  }
    67  
    68  func TestHiddenRotateGregor(t *testing.T) {
    69  	tt := newTeamTester(t)
    70  	defer tt.cleanup()
    71  
    72  	tt.addUser("onr")
    73  	tt.addUser("adm")
    74  
    75  	id, name := tt.users[0].createTeam2()
    76  	tt.users[0].addTeamMember(name.String(), tt.users[1].username, keybase1.TeamRole_ADMIN)
    77  
    78  	assertGen := func(g keybase1.PerTeamKeyGeneration) bool {
    79  		team, err := teams.Load(context.TODO(), tt.users[1].tc.G, keybase1.LoadTeamArg{
    80  			Name:    name.String(),
    81  			StaleOK: true,
    82  		})
    83  		require.NoError(t, err)
    84  		key, err := team.ApplicationKey(context.TODO(), keybase1.TeamApplication_CHAT)
    85  		require.NoError(t, err)
    86  		return (key.KeyGeneration == g)
    87  	}
    88  	assertGenFTL := func(g keybase1.PerTeamKeyGeneration) bool {
    89  		mctx := libkb.NewMetaContextForTest(*tt.users[1].tc)
    90  		team, err := mctx.G().GetFastTeamLoader().Load(mctx, keybase1.FastTeamLoadArg{
    91  			ID:            id,
    92  			NeedLatestKey: true,
    93  			Applications:  []keybase1.TeamApplication{keybase1.TeamApplication_CHAT},
    94  		})
    95  		require.NoError(t, err)
    96  		require.Equal(t, 1, len(team.ApplicationKeys))
    97  		return (team.ApplicationKeys[0].KeyGeneration == g)
    98  	}
    99  	// Prime user 1's cache
   100  	ok := assertGen(keybase1.PerTeamKeyGeneration(1))
   101  	require.True(t, ok)
   102  	ok = assertGenFTL(keybase1.PerTeamKeyGeneration(1))
   103  	require.True(t, ok)
   104  
   105  	err := teams.RotateKey(context.TODO(), tt.users[0].tc.G, keybase1.TeamRotateKeyArg{TeamID: id, Rt: keybase1.RotationType_HIDDEN})
   106  	require.NoError(t, err)
   107  
   108  	// Poll for an update, user 1 should get it as soon as gregor tells us to bust our cache.
   109  	pollForTrue(t, tt.users[1].tc.G, func(i int) bool {
   110  		return assertGen(keybase1.PerTeamKeyGeneration(2))
   111  	})
   112  
   113  	err = teams.RotateKey(context.TODO(), tt.users[0].tc.G, keybase1.TeamRotateKeyArg{TeamID: id, Rt: keybase1.RotationType_HIDDEN})
   114  	require.NoError(t, err)
   115  
   116  	// Poll for an update to FTL, user 1 should get it as soon as gregor tells us to bust our cache.
   117  	pollForTrue(t, tt.users[1].tc.G, func(i int) bool {
   118  		return assertGenFTL(keybase1.PerTeamKeyGeneration(3))
   119  	})
   120  }
   121  
   122  func TestTeamRotateOnRevoke(t *testing.T) {
   123  	tt := newTeamTester(t)
   124  	defer tt.cleanup()
   125  
   126  	tt.addUser("onr")
   127  	tt.addUserWithPaper("wtr")
   128  
   129  	teamID, teamName := tt.users[0].createTeam2()
   130  	tt.users[0].addTeamMember(teamName.String(), tt.users[1].username, keybase1.TeamRole_WRITER)
   131  
   132  	// get the before state of the team
   133  	before, err := GetTeamForTestByStringName(context.TODO(), tt.users[0].tc.G, teamName.String())
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  	if before.Generation() != 1 {
   138  		t.Errorf("generation before rotate: %d, expected 1", before.Generation())
   139  	}
   140  	secretBefore := before.Data.PerTeamKeySeedsUnverified[before.Generation()].Seed.ToBytes()
   141  
   142  	// User1 should get a gregor that the team he was just added to changed.
   143  	tt.users[1].waitForTeamChangedGregor(teamID, keybase1.Seqno(2))
   144  	// User0 should get a (redundant) gregor notification that
   145  	// he just changed the team.
   146  	tt.users[0].waitForTeamChangedGregor(teamID, keybase1.Seqno(2))
   147  
   148  	tt.users[1].revokePaperKey()
   149  	tt.users[0].waitForAnyRotateByID(teamID, keybase1.Seqno(2) /* toSeqno */, keybase1.Seqno(1) /* toHiddenSeqno */)
   150  
   151  	// check that key was rotated for team
   152  	after, err := GetTeamForTestByStringName(context.TODO(), tt.users[0].tc.G, teamName.String())
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	if after.Generation() != 2 {
   157  		t.Errorf("generation after rotate: %d, expected 2", after.Generation())
   158  	}
   159  	secretAfter := after.Data.PerTeamKeySeedsUnverified[after.Generation()].Seed.ToBytes()
   160  	if libkb.SecureByteArrayEq(secretAfter, secretBefore) {
   161  		t.Fatal("team secret did not change when rotated")
   162  	}
   163  }
   164  
   165  type teamTester struct {
   166  	t     *testing.T
   167  	users []*userPlusDevice
   168  }
   169  
   170  func newTeamTester(t *testing.T) *teamTester {
   171  	return &teamTester{t: t}
   172  }
   173  
   174  func (tt *teamTester) addUser(pre string) *userPlusDevice {
   175  	return tt.addUserHelper(pre, true, false)
   176  }
   177  
   178  func (tt *teamTester) addUserWithPaper(pre string) *userPlusDevice {
   179  	return tt.addUserHelper(pre, true, true)
   180  }
   181  
   182  func (tt *teamTester) addPuklessUser(pre string) *userPlusDevice {
   183  	return tt.addUserHelper(pre, false, false)
   184  }
   185  
   186  func (tt *teamTester) logUserNames() {
   187  	for _, u := range tt.users {
   188  		var pukless string
   189  		if u.device.tctx.Tp.DisableUpgradePerUserKey {
   190  			pukless = "pukless "
   191  		}
   192  		tt.t.Logf("Signed up %s%q (%s)", pukless, u.username, u.uid)
   193  	}
   194  }
   195  
   196  func installInsecureTriplesec(g *libkb.GlobalContext) {
   197  	g.NewTriplesec = func(passphrase []byte, salt []byte) (libkb.Triplesec, error) {
   198  		warner := func() { g.Log.Warning("Installing insecure Triplesec with weak stretch parameters") }
   199  		isProduction := func() bool {
   200  			return g.Env.GetRunMode() == libkb.ProductionRunMode
   201  		}
   202  		return insecureTriplesec.NewCipher(passphrase, salt, libkb.ClientTriplesecVersion, warner, isProduction)
   203  	}
   204  }
   205  
   206  func (tt *teamTester) addUserHelper(pre string, puk bool, paper bool) *userPlusDevice {
   207  	tctx := setupTest(tt.t, pre)
   208  	if !puk {
   209  		tctx.Tp.DisableUpgradePerUserKey = true
   210  	}
   211  
   212  	var u userPlusDevice
   213  	u.device = &deviceWrapper{tctx: tctx}
   214  	u.device.start(0)
   215  
   216  	userInfo := randomUser(pre)
   217  	require.True(tt.t, libkb.CheckUsername.F(userInfo.username), "username check failed (%v): %v", libkb.CheckUsername.Hint, userInfo.username)
   218  	tc := u.device.tctx
   219  	g := tc.G
   220  	installInsecureTriplesec(g)
   221  
   222  	signupUI := signupUI{
   223  		info:         userInfo,
   224  		Contextified: libkb.NewContextified(g),
   225  	}
   226  	g.SetUI(&signupUI)
   227  	signup := client.NewCmdSignupRunner(g)
   228  	signup.SetTestWithPaper(paper)
   229  	require.NoError(tt.t, signup.Run())
   230  	tt.t.Logf("signed up %s", userInfo.username)
   231  
   232  	u.tc = tc
   233  	u.userInfo = userInfo
   234  	u.username = userInfo.username
   235  	u.passphrase = userInfo.passphrase
   236  	u.uid = libkb.UsernameToUID(u.username)
   237  
   238  	cli, xp, err := client.GetRPCClientWithContext(g)
   239  	require.NoError(tt.t, err)
   240  
   241  	u.deviceClient = keybase1.DeviceClient{Cli: cli}
   242  	u.device.userClient = keybase1.UserClient{Cli: cli}
   243  	u.device.accountClient = keybase1.AccountClient{Cli: cli}
   244  
   245  	// register for notifications
   246  	u.notifications = newTeamNotifyHandler()
   247  	srv := rpc.NewServer(xp, nil)
   248  	err = srv.Register(keybase1.NotifyTeamProtocol(u.notifications))
   249  	require.NoError(tt.t, err)
   250  	err = srv.Register(keybase1.NotifyBadgesProtocol(u.notifications))
   251  	require.NoError(tt.t, err)
   252  	err = srv.Register(keybase1.NotifyEphemeralProtocol(u.notifications))
   253  	require.NoError(tt.t, err)
   254  	err = srv.Register(keybase1.NotifyTeambotProtocol(u.notifications))
   255  	require.NoError(tt.t, err)
   256  	ncli := keybase1.NotifyCtlClient{Cli: cli}
   257  	err = ncli.SetNotifications(context.TODO(), keybase1.NotificationChannels{
   258  		Team:      true,
   259  		Badges:    true,
   260  		Ephemeral: true,
   261  		Teambot:   true,
   262  	})
   263  	require.NoError(tt.t, err)
   264  
   265  	// register fake teams UI for tests
   266  	err = srv.Register(keybase1.TeamsUiProtocol(&teamsUI{}))
   267  	require.NoError(tt.t, err)
   268  
   269  	u.teamsClient = keybase1.TeamsClient{Cli: cli}
   270  	u.userClient = keybase1.UserClient{Cli: cli}
   271  	u.stellarClient = newStellarRetryClient(cli)
   272  
   273  	err = g.ConfigureConfig()
   274  	require.NoError(tt.t, err)
   275  
   276  	devices, backups := u.device.loadEncryptionKIDs()
   277  	require.Len(tt.t, devices, 1, "devices")
   278  	u.device.deviceKey.KID = devices[0]
   279  	require.True(tt.t, u.device.deviceKey.KID.Exists())
   280  	if paper {
   281  		require.Len(tt.t, backups, 1, "backup keys")
   282  		u.backupKey = &backups[0]
   283  		u.backupKey.secret = signupUI.info.displayedPaperKey
   284  	} else {
   285  		require.Len(tt.t, backups, 0, "backup keys")
   286  	}
   287  
   288  	tt.users = append(tt.users, &u)
   289  	return &u
   290  }
   291  
   292  func (tt *teamTester) cleanup() {
   293  	for _, u := range tt.users {
   294  		u.device.tctx.Cleanup()
   295  		if u.device.service != nil {
   296  			u.tc.T.Logf("in teamTester cleanup, stopping test user's service")
   297  			u.device.service.Stop(0)
   298  			err := u.device.stop()
   299  			require.NoError(u.tc.T, err)
   300  			u.tc.T.Logf("in teamTester cleanup, stopped test user's service")
   301  		}
   302  	}
   303  }
   304  
   305  type userPlusDevice struct {
   306  	uid                      keybase1.UID
   307  	username                 string
   308  	passphrase               string
   309  	userInfo                 *signupInfo
   310  	backupKey                *backupKey
   311  	device                   *deviceWrapper
   312  	tc                       *libkb.TestContext
   313  	deviceClient             keybase1.DeviceClient
   314  	teamsClient              keybase1.TeamsClient
   315  	userClient               keybase1.UserClient
   316  	stellarClient            stellar1.LocalInterface
   317  	notifications            *teamNotifyHandler
   318  	suppressTeamChatAnnounce bool
   319  }
   320  
   321  func (u *userPlusDevice) createTeam() string {
   322  	create := client.NewCmdTeamCreateRunner(u.tc.G)
   323  	nameStr, err := libkb.RandString("tt", 5)
   324  	if err != nil {
   325  		u.tc.T.Fatal(err)
   326  	}
   327  	name, err := keybase1.TeamNameFromString(strings.ToLower(nameStr))
   328  	if err != nil {
   329  		u.tc.T.Fatal(err)
   330  	}
   331  	create.TeamName = name
   332  	tracer := u.tc.G.CTimeTracer(context.Background(), "tracer-create-team", true)
   333  	defer tracer.Finish()
   334  	if err := create.Run(); err != nil {
   335  		u.tc.T.Fatal(err)
   336  	}
   337  	return create.TeamName.String()
   338  }
   339  
   340  func (u *userPlusDevice) createTeam2() (teamID keybase1.TeamID, teamName keybase1.TeamName) {
   341  	name := u.createTeam()
   342  	team, err := teams.Load(context.Background(), u.tc.G, keybase1.LoadTeamArg{
   343  		Name: name,
   344  	})
   345  	require.NoError(u.tc.T, err)
   346  	return team.ID, team.Name()
   347  }
   348  
   349  func (u *userPlusDevice) teamSetSettings(id keybase1.TeamID, settings keybase1.TeamSettings) {
   350  	err := u.teamsClient.TeamSetSettings(context.Background(), keybase1.TeamSetSettingsArg{
   351  		TeamID:   id,
   352  		Settings: settings,
   353  	})
   354  	require.NoError(u.tc.T, err)
   355  	if u.notifications != nil {
   356  		changeByID := false
   357  		for {
   358  			select {
   359  			case arg := <-u.notifications.changeCh:
   360  				changeByID = arg.Changes.Misc
   361  			case <-time.After(500 * time.Millisecond * libkb.CITimeMultiplier(u.tc.G)):
   362  				u.tc.T.Fatal("no notification on teamSetSettings")
   363  			}
   364  			if changeByID {
   365  				return
   366  			}
   367  		}
   368  	}
   369  }
   370  
   371  func (u *userPlusDevice) teamGetDetails(teamName string) keybase1.TeamDetails {
   372  	res, err := u.teamsClient.TeamGet(context.Background(), keybase1.TeamGetArg{
   373  		Name: teamName,
   374  	})
   375  	require.NoError(u.tc.T, err)
   376  	return res
   377  }
   378  
   379  func (u *userPlusDevice) addRestrictedBotTeamMember(team, username string, botSettings keybase1.TeamBotSettings) {
   380  	add := client.NewCmdTeamAddMemberRunner(u.tc.G)
   381  	add.Team = team
   382  	add.Username = username
   383  	add.Role = keybase1.TeamRole_RESTRICTEDBOT
   384  	add.BotSettings = &botSettings
   385  	add.SkipChatNotification = u.suppressTeamChatAnnounce
   386  	err := add.Run()
   387  	require.NoError(u.tc.T, err)
   388  }
   389  
   390  func (u *userPlusDevice) addTeamMember(team, username string, role keybase1.TeamRole) {
   391  	if role.IsRestrictedBot() {
   392  		require.Fail(u.tc.T, "use addRestrictedBotTeamMember instead")
   393  	}
   394  	add := client.NewCmdTeamAddMemberRunner(u.tc.G)
   395  	add.Team = team
   396  	add.Username = username
   397  	add.Role = role
   398  	add.SkipChatNotification = u.suppressTeamChatAnnounce
   399  	err := add.Run()
   400  	require.NoError(u.tc.T, err)
   401  }
   402  
   403  func (u *userPlusDevice) removeTeamMember(team, username string) {
   404  	rm := client.NewCmdTeamRemoveMemberRunner(u.tc.G)
   405  	rm.Team = team
   406  	rm.Assertion = username
   407  	rm.Force = true
   408  	err := rm.Run()
   409  	require.NoError(u.tc.T, err)
   410  }
   411  
   412  func (u *userPlusDevice) leave(team string) {
   413  	leave := client.NewCmdTeamLeaveRunner(u.tc.G)
   414  	leave.Team = team
   415  	err := leave.Run()
   416  	require.NoError(u.tc.T, err)
   417  }
   418  
   419  func (u *userPlusDevice) changeTeamMember(team, username string, role keybase1.TeamRole) {
   420  	change := client.NewCmdTeamEditMemberRunner(u.tc.G)
   421  	change.Team = team
   422  	change.Username = username
   423  	change.Role = role
   424  	err := change.Run()
   425  	require.NoError(u.tc.T, err)
   426  }
   427  
   428  func (u *userPlusDevice) addTeamMemberEmail(team, email string, role keybase1.TeamRole) {
   429  	add := client.NewCmdTeamAddMemberRunner(u.tc.G)
   430  	add.Team = team
   431  	add.Email = email
   432  	add.Role = role
   433  	err := add.Run()
   434  	require.NoError(u.tc.T, err)
   435  }
   436  
   437  func (u *userPlusDevice) reAddUserAfterReset(team keybase1.TeamID, w *userPlusDevice) {
   438  	err := u.teamsClient.TeamReAddMemberAfterReset(context.Background(),
   439  		keybase1.TeamReAddMemberAfterResetArg{
   440  			Id:       team,
   441  			Username: w.username,
   442  		})
   443  	require.NoError(u.tc.T, err)
   444  }
   445  
   446  func (u *userPlusDevice) loadTeam(teamname string, admin bool) *teams.Team {
   447  	team, err := teams.Load(context.Background(), u.tc.G, keybase1.LoadTeamArg{
   448  		Name:        teamname,
   449  		NeedAdmin:   admin,
   450  		ForceRepoll: true,
   451  	})
   452  	require.NoError(u.tc.T, err)
   453  	return team
   454  }
   455  
   456  func (u *userPlusDevice) loadTeamByID(teamID keybase1.TeamID, admin bool) *teams.Team {
   457  	team, err := teams.Load(context.Background(), u.tc.G, keybase1.LoadTeamArg{
   458  		ID:          teamID,
   459  		Public:      teamID.IsPublic(),
   460  		NeedAdmin:   admin,
   461  		ForceRepoll: true,
   462  	})
   463  	require.NoError(u.tc.T, err)
   464  	return team
   465  }
   466  
   467  func (u *userPlusDevice) readInviteEmails(email string) []string {
   468  	mctx := u.MetaContext()
   469  	arg := libkb.NewAPIArg("test/team/get_tokens")
   470  	arg.Args = libkb.NewHTTPArgs()
   471  	arg.Args.Add("email", libkb.S{Val: email})
   472  	res, err := u.tc.G.API.Get(mctx, arg)
   473  	if err != nil {
   474  		u.tc.T.Fatal(err)
   475  	}
   476  	tokens := res.Body.AtKey("tokens")
   477  	n, err := tokens.Len()
   478  	if err != nil {
   479  		u.tc.T.Fatal(err)
   480  	}
   481  	if n == 0 {
   482  		require.Fail(u.tc.T, fmt.Sprintf("no invite tokens for %s", email))
   483  	}
   484  
   485  	exp := make([]string, n)
   486  	for i := 0; i < n; i++ {
   487  		token, err := tokens.AtIndex(i).GetString()
   488  		if err != nil {
   489  			u.tc.T.Fatal(err)
   490  		}
   491  		exp[i] = token
   492  	}
   493  
   494  	return exp
   495  }
   496  
   497  func (u *userPlusDevice) acceptEmailInvite(token string) {
   498  	c := client.NewCmdTeamAcceptInviteRunner(u.tc.G)
   499  	c.Token = token
   500  	if err := c.Run(); err != nil {
   501  		u.tc.T.Fatal(err)
   502  	}
   503  }
   504  
   505  func (u *userPlusDevice) acceptInviteOrRequestAccess(tokenOrName string) keybase1.TeamAcceptOrRequestResult {
   506  	tui := &teamsUI{}
   507  	ret, err := teams.TeamAcceptInviteOrRequestAccess(context.TODO(), u.tc.G, tui, tokenOrName)
   508  	require.NoError(u.tc.T, err)
   509  	return ret
   510  }
   511  
   512  func (u *userPlusDevice) teamList(userAssertion string, all, includeImplicitTeams bool) keybase1.AnnotatedTeamList {
   513  	cli := u.teamsClient
   514  	res, err := cli.TeamListUnverified(context.TODO(), keybase1.TeamListUnverifiedArg{
   515  		UserAssertion:        userAssertion,
   516  		IncludeImplicitTeams: includeImplicitTeams,
   517  	})
   518  	require.NoError(u.tc.T, err)
   519  	return res
   520  }
   521  
   522  func (u *userPlusDevice) revokePaperKey() {
   523  	id := u.paperKeyID()
   524  
   525  	runner := client.NewCmdDeviceRemoveRunner(u.tc.G)
   526  	runner.SetIDOrName(id.String())
   527  	if err := runner.Run(); err != nil {
   528  		u.tc.T.Fatal(err)
   529  	}
   530  }
   531  
   532  func (u *userPlusDevice) devices() []keybase1.Device {
   533  	d, err := u.deviceClient.DeviceList(context.TODO(), 0)
   534  	if err != nil {
   535  		u.tc.T.Fatal(err)
   536  	}
   537  	return d
   538  }
   539  
   540  func (u *userPlusDevice) userVersion() keybase1.UserVersion {
   541  	uv, err := u.device.userClient.MeUserVersion(context.TODO(), keybase1.MeUserVersionArg{ForcePoll: true})
   542  	require.NoError(u.tc.T, err)
   543  	return uv
   544  }
   545  
   546  func (u *userPlusDevice) paperKeyID() keybase1.DeviceID {
   547  	for _, d := range u.devices() {
   548  		if d.Type == keybase1.DeviceTypeV2_PAPER {
   549  			return d.DeviceID
   550  		}
   551  	}
   552  	u.tc.T.Fatal("no paper key found")
   553  	return keybase1.DeviceID("")
   554  }
   555  
   556  func (u *userPlusDevice) waitForTeamChangedGregor(teamID keybase1.TeamID, toSeqno keybase1.Seqno) {
   557  	// process 10 team rotations or 10s worth of time
   558  	for i := 0; i < 10; i++ {
   559  		select {
   560  		case arg := <-u.notifications.changeCh:
   561  			u.tc.T.Logf("membership change received: %+v", arg)
   562  			if arg.TeamID.Eq(teamID) && arg.Changes.MembershipChanged && !arg.Changes.KeyRotated && !arg.Changes.Renamed && arg.LatestSeqno == toSeqno {
   563  				u.tc.T.Logf("change matched!")
   564  				return
   565  			}
   566  			u.tc.T.Logf("ignoring change message (expected teamID = %q, seqno = %d)", teamID.String(), toSeqno)
   567  		case <-time.After(1 * time.Second * libkb.CITimeMultiplier(u.tc.G)):
   568  		}
   569  	}
   570  	require.Fail(u.tc.T, fmt.Sprintf("timed out waiting for team rotate %s", teamID))
   571  }
   572  
   573  func (u *userPlusDevice) waitForMetadataUpdateGregor(reason string) {
   574  	// process 10 team rotations or 10s worth of time
   575  	for i := 0; i < 10; i++ {
   576  		select {
   577  		case <-u.notifications.metadataUpdateCh:
   578  			u.tc.T.Logf("metadata update received for reason %q", reason)
   579  			return
   580  		case <-time.After(1 * time.Second * libkb.CITimeMultiplier(u.tc.G)):
   581  		}
   582  	}
   583  	require.Fail(u.tc.T, fmt.Sprintf("timed out waiting for metadata update for reason %q", reason))
   584  }
   585  
   586  func (u *userPlusDevice) waitForBadgeStateWithReset(numReset int) keybase1.BadgeState {
   587  	// Process any number of badge state updates, but bail out after
   588  	// 10 seconds.
   589  	timeout := time.After(10 * time.Second * libkb.CITimeMultiplier(u.tc.G))
   590  	i := 0
   591  	for {
   592  		select {
   593  		case arg := <-u.notifications.badgeCh:
   594  			u.tc.T.Logf("badge state received %d: %+v", i, arg.TeamsWithResetUsers)
   595  			i++
   596  			if len(arg.TeamsWithResetUsers) == numReset {
   597  				u.tc.T.Logf("badge state length match")
   598  				return arg
   599  			}
   600  		case <-timeout:
   601  			u.tc.T.Fatal("timed out waiting for badge state")
   602  			return keybase1.BadgeState{}
   603  		}
   604  	}
   605  }
   606  
   607  func (u *userPlusDevice) drainGregor() {
   608  	for i := 0; i < 1000; i++ {
   609  		select {
   610  		case <-u.notifications.changeCh:
   611  			u.tc.T.Logf("dropped notification")
   612  			// drop
   613  		case <-time.After(500 * time.Millisecond * libkb.CITimeMultiplier(u.tc.G)):
   614  			u.tc.T.Logf("no notification received, drain complete")
   615  			return
   616  		}
   617  	}
   618  }
   619  
   620  func (u *userPlusDevice) waitForRotateByID(teamID keybase1.TeamID, toSeqno keybase1.Seqno) {
   621  	u.waitForAnyRotateByID(teamID, toSeqno, keybase1.Seqno(0))
   622  }
   623  
   624  func (u *userPlusDevice) waitForAnyRotateByID(teamID keybase1.TeamID, toSeqno keybase1.Seqno, toHiddenSeqno keybase1.Seqno) {
   625  	u.tc.T.Logf("waiting for team rotate %s", teamID)
   626  
   627  	// jump start the clkr queue processing loop
   628  	u.kickTeamRekeyd()
   629  
   630  	// process 20 team rotate notifications or 10s worth of time
   631  	timeout := time.After(10 * time.Second * libkb.CITimeMultiplier(u.tc.G))
   632  	for i := 0; i < 20; i++ {
   633  		select {
   634  		case arg := <-u.notifications.changeCh:
   635  			u.tc.T.Logf("rotate received: %s", spew.Sdump(arg))
   636  			if arg.TeamID.Eq(teamID) && arg.Changes.KeyRotated && arg.LatestSeqno == toSeqno && (toHiddenSeqno == keybase1.Seqno(0) || toHiddenSeqno == arg.LatestHiddenSeqno) {
   637  				u.tc.T.Logf("rotate matched!")
   638  				return
   639  			}
   640  			u.tc.T.Logf("ignoring rotate message")
   641  		case <-timeout:
   642  			require.Fail(u.tc.T, fmt.Sprintf("timed out waiting for team rotate %s", teamID))
   643  			return
   644  		}
   645  	}
   646  }
   647  
   648  func (u *userPlusDevice) waitForTeamChangedAndRotated(teamID keybase1.TeamID, toSeqno keybase1.Seqno) {
   649  	// process 20 team rotate notifications or 10s worth of time
   650  	timeout := time.After(10 * time.Second * libkb.CITimeMultiplier(u.tc.G))
   651  	for i := 0; i < 20; i++ {
   652  		select {
   653  		case arg := <-u.notifications.changeCh:
   654  			u.tc.T.Logf("membership change received: %+v", arg)
   655  			if arg.TeamID.Eq(teamID) && arg.Changes.MembershipChanged && arg.Changes.KeyRotated && !arg.Changes.Renamed && arg.LatestSeqno == toSeqno {
   656  				u.tc.T.Logf("change matched!")
   657  				return
   658  			}
   659  			u.tc.T.Logf("ignoring change message (expected team = %v, seqno = %d)", teamID, toSeqno)
   660  		case <-timeout:
   661  			require.Fail(u.tc.T, fmt.Sprintf("timed out waiting for team rotate %s", teamID))
   662  			return
   663  		}
   664  	}
   665  }
   666  
   667  func (u *userPlusDevice) waitForTeamChangeRenamed(teamID keybase1.TeamID) {
   668  	// Process any number of badge state updates, but bail out after 10
   669  	// seconds.
   670  	timeout := time.After(10 * time.Second * libkb.CITimeMultiplier(u.tc.G))
   671  	i := 0
   672  	for {
   673  		select {
   674  		case arg := <-u.notifications.changeCh:
   675  			u.tc.T.Logf("membership change received: %+v", arg)
   676  			if arg.TeamID.Eq(teamID) && !arg.Changes.MembershipChanged && !arg.Changes.KeyRotated && arg.Changes.Renamed {
   677  				u.tc.T.Logf("change matched!")
   678  				return
   679  			}
   680  			u.tc.T.Logf("ignoring change message attempt %d (expected team = %v, renamed = true)", i, teamID)
   681  		case <-timeout:
   682  			require.Fail(u.tc.T, fmt.Sprintf("timed out waiting for team changes %s", teamID))
   683  		}
   684  		i++
   685  	}
   686  }
   687  
   688  func (u *userPlusDevice) waitForNewlyAddedToTeamByID(teamID keybase1.TeamID) {
   689  	u.tc.T.Logf("waiting for newly added to team %s", teamID)
   690  
   691  	// Process any number of badge state updates, but bail out after 10
   692  	// seconds.
   693  	timeout := time.After(10 * time.Second * libkb.CITimeMultiplier(u.tc.G))
   694  	i := 0
   695  	for {
   696  		select {
   697  		case tid := <-u.notifications.newlyAddedToTeam:
   698  			u.tc.T.Logf("newlyAddedToTeam recieved: %+v", tid)
   699  			if tid.Eq(teamID) {
   700  				u.tc.T.Logf("newlyAddedToTeam matched!")
   701  				return
   702  			}
   703  			u.tc.T.Logf("ignoring newly added message attempt %d (expected team = %v)", i, teamID)
   704  		case <-timeout:
   705  			require.Fail(u.tc.T, fmt.Sprintf("timed out waiting newly added message %s", teamID))
   706  		}
   707  	}
   708  }
   709  
   710  func (u *userPlusDevice) pollForTeamSeqnoLink(team string, toSeqno keybase1.Seqno) {
   711  	for i := 0; i < 20; i++ {
   712  		after, err := teams.Load(context.TODO(), u.tc.G, keybase1.LoadTeamArg{
   713  			Name:        team,
   714  			ForceRepoll: true,
   715  		})
   716  		if err != nil {
   717  			require.Fail(u.tc.T, fmt.Sprintf("error while loading team %q: %v", team, err))
   718  		}
   719  
   720  		if after.CurrentSeqno() >= toSeqno {
   721  			u.tc.T.Logf("Found new seqno %d at poll loop iter %d", after.CurrentSeqno(), i)
   722  			return
   723  		}
   724  
   725  		time.Sleep(500 * time.Millisecond * libkb.CITimeMultiplier(u.tc.G))
   726  	}
   727  
   728  	require.Fail(u.tc.T, fmt.Sprintf("timed out waiting for team rotate %s", team))
   729  }
   730  
   731  func (u *userPlusDevice) pollForTeamSeqnoLinkWithLoadArgs(args keybase1.LoadTeamArg, toSeqno keybase1.Seqno) {
   732  	args.ForceRepoll = true
   733  	for i := 0; i < 20; i++ {
   734  		details, err := teams.Load(context.Background(), u.tc.G, args)
   735  		if err != nil {
   736  			require.Fail(u.tc.T, fmt.Sprintf("error while loading team %v: %v", args, err))
   737  		}
   738  
   739  		if details.CurrentSeqno() >= toSeqno {
   740  			u.tc.T.Logf("Found new seqno %d at poll loop iter %d", details.CurrentSeqno(), i)
   741  			return
   742  		}
   743  
   744  		time.Sleep(500 * time.Millisecond * libkb.CITimeMultiplier(u.tc.G))
   745  	}
   746  
   747  	require.Fail(u.tc.T, fmt.Sprintf("timed out waiting for team %v seqno link %d", args, toSeqno))
   748  }
   749  
   750  func (u *userPlusDevice) proveRooter() {
   751  	cmd := client.NewCmdProveRooterRunner(u.tc.G, u.username)
   752  	if err := cmd.Run(); err != nil {
   753  		u.tc.T.Fatal(err)
   754  	}
   755  }
   756  
   757  func (u *userPlusDevice) proveGubbleSocial() {
   758  	proveGubbleUniverse(u.tc, "gubble.social", "gubble_social", u.username, u.newSecretUI())
   759  }
   760  
   761  func (u *userPlusDevice) revokeServiceProof(serviceType string) {
   762  	tctx := u.tc
   763  	arg := libkb.NewLoadUserArg(tctx.G).WithUID(u.uid).WithForcePoll(true)
   764  	user, err := libkb.LoadUser(arg)
   765  	require.NoError(tctx.T, err)
   766  	require.NotNil(tctx.T, user)
   767  
   768  	st := tctx.G.GetProofServices().GetServiceType(context.TODO(), "rooter")
   769  	ret := user.IDTable().GetActiveProofsFor(st)
   770  	require.Len(tctx.T, ret, 1)
   771  	sigID := ret[0].GetSigID()
   772  
   773  	revokeClient := keybase1.RevokeClient{Cli: u.teamsClient.Cli}
   774  	err = revokeClient.RevokeSigs(context.TODO(), keybase1.RevokeSigsArg{
   775  		SigIDQueries: []string{sigID.String()},
   776  	})
   777  	require.NoError(tctx.T, err)
   778  }
   779  
   780  func (u *userPlusDevice) track(username string) {
   781  	trackCmd := client.NewCmdTrackRunner(u.tc.G)
   782  	trackCmd.SetUser(username)
   783  	trackCmd.SetOptions(keybase1.TrackOptions{BypassConfirm: true})
   784  	err := trackCmd.Run()
   785  	require.NoError(u.tc.T, err)
   786  }
   787  
   788  func (u *userPlusDevice) block(username string, chat bool, follow bool) {
   789  	arg := keybase1.SetUserBlocksArg{
   790  		Blocks: []keybase1.UserBlockArg{
   791  			{
   792  				Username:       username,
   793  				SetChatBlock:   &chat,
   794  				SetFollowBlock: &follow,
   795  			},
   796  		},
   797  	}
   798  	err := u.device.userClient.SetUserBlocks(context.TODO(), arg)
   799  	require.NoError(u.tc.T, err)
   800  }
   801  
   802  func (u *userPlusDevice) untrack(username string) {
   803  	untrackCmd := client.NewCmdUntrackRunner(u.tc.G)
   804  	untrackCmd.SetUser(username)
   805  	err := untrackCmd.Run()
   806  	require.NoError(u.tc.T, err)
   807  }
   808  
   809  func (u *userPlusDevice) kickTeamRekeyd() {
   810  	kickTeamRekeyd(u.tc.G, u.tc.T)
   811  }
   812  
   813  func (u *userPlusDevice) kickAutoresetd() {
   814  	kickAutoresetd(u.tc.G, u.tc.T)
   815  }
   816  
   817  func (u *userPlusDevice) lookupImplicitTeam(create bool, displayName string, public bool) (keybase1.TeamID, error) {
   818  	res, err := u.lookupImplicitTeam2(create, displayName, public)
   819  	return res.TeamID, err
   820  }
   821  
   822  func (u *userPlusDevice) lookupImplicitTeam2(create bool, displayName string, public bool) (keybase1.LookupImplicitTeamRes, error) {
   823  	cli := u.teamsClient
   824  	var err error
   825  	var res keybase1.LookupImplicitTeamRes
   826  	if create {
   827  		res, err = cli.LookupOrCreateImplicitTeam(context.TODO(), keybase1.LookupOrCreateImplicitTeamArg{Name: displayName, Public: public})
   828  	} else {
   829  		res, err = cli.LookupImplicitTeam(context.TODO(), keybase1.LookupImplicitTeamArg{Name: displayName, Public: public})
   830  	}
   831  	return res, err
   832  }
   833  
   834  func (u *userPlusDevice) delayMerkleTeam(teamID keybase1.TeamID) {
   835  	mctx := u.MetaContext()
   836  	_, err := u.tc.G.API.Post(mctx, libkb.APIArg{
   837  		Endpoint: "test/merkled/delay_team",
   838  		Args: libkb.HTTPArgs{
   839  			"tid": libkb.S{Val: teamID.String()},
   840  		},
   841  		SessionType: libkb.APISessionTypeREQUIRED,
   842  	})
   843  	require.NoError(u.tc.T, err)
   844  }
   845  
   846  func (u *userPlusDevice) newSecretUI() *libkb.TestSecretUI {
   847  	return &libkb.TestSecretUI{Passphrase: u.passphrase}
   848  }
   849  
   850  func (u *userPlusDevice) provisionNewDevice() (d *deviceWrapper, cleanup func()) {
   851  	tc := setupTest(u.tc.T, "sub")
   852  	t := tc.T
   853  	g := tc.G
   854  
   855  	device := &deviceWrapper{tctx: tc}
   856  	device.start(0)
   857  
   858  	require.NotNil(t, u.backupKey, "Add user with paper key to use provisionNewDevice")
   859  
   860  	// ui for provisioning
   861  	ui := &rekeyProvisionUI{username: u.username, backupKey: *u.backupKey}
   862  	{
   863  		_, xp, err := client.GetRPCClientWithContext(g)
   864  		require.NoError(t, err)
   865  		srv := rpc.NewServer(xp, nil)
   866  		protocols := []rpc.Protocol{
   867  			keybase1.LoginUiProtocol(ui),
   868  			keybase1.SecretUiProtocol(ui),
   869  			keybase1.ProvisionUiProtocol(ui),
   870  		}
   871  		for _, prot := range protocols {
   872  			err = srv.Register(prot)
   873  			require.NoError(t, err)
   874  		}
   875  	}
   876  
   877  	cmd := client.NewCmdLoginRunner(g)
   878  	err := cmd.Run()
   879  	require.NoError(t, err, "login")
   880  
   881  	// Clear the paper key.
   882  	g.ActiveDevice.ClearCaches()
   883  
   884  	skey, err := g.ActiveDevice.SigningKey()
   885  	require.NoError(t, err)
   886  	device.deviceKey.KID = skey.GetKID()
   887  	require.True(t, device.deviceKey.KID.Exists())
   888  	device.deviceKey.DeviceID = g.ActiveDevice.DeviceID()
   889  	require.True(t, device.deviceKey.DeviceID.Exists())
   890  
   891  	cleanup = func() {
   892  		device.tctx.Cleanup()
   893  		if device.service != nil {
   894  			device.service.Stop(0)
   895  			err := device.stop()
   896  			require.NoError(tc.T, err)
   897  		}
   898  	}
   899  
   900  	return device, cleanup
   901  }
   902  
   903  func (u *userPlusDevice) reset() {
   904  	u.device.tctx.Tp.SkipLogoutIfRevokedCheck = true
   905  	uvBefore := u.userVersion()
   906  	err := u.device.accountClient.ResetAccount(context.TODO(), keybase1.ResetAccountArg{Passphrase: u.passphrase})
   907  	require.NoError(u.tc.T, err)
   908  	uvAfter := u.userVersion()
   909  	require.NotEqual(u.tc.T, uvBefore.EldestSeqno, uvAfter.EldestSeqno,
   910  		"eldest seqno should change as result of reset")
   911  	u.tc.T.Logf("User reset; eldest seqno %d -> %d", uvBefore.EldestSeqno, uvAfter.EldestSeqno)
   912  }
   913  
   914  func (u *userPlusDevice) delete() {
   915  	g := u.tc.G
   916  	ui := genericUI{
   917  		g:          g,
   918  		SecretUI:   signupInfoSecretUI{u.userInfo, u.tc.G.GetLog()},
   919  		TerminalUI: smuTerminalUI{},
   920  	}
   921  	g.SetUI(&ui)
   922  	cmd := client.NewCmdAccountDeleteRunner(g)
   923  	err := cmd.Run()
   924  	require.NoError(u.tc.T, err)
   925  }
   926  
   927  func (u *userPlusDevice) logout() {
   928  	err := u.tc.Logout()
   929  	require.NoError(u.tc.T, err)
   930  }
   931  
   932  func (u *userPlusDevice) login() {
   933  	uis := libkb.UIs{
   934  		ProvisionUI: &kbtest.TestProvisionUI{},
   935  		LogUI:       u.tc.G.Log,
   936  		GPGUI:       &kbtest.GPGTestUI{},
   937  		SecretUI:    u.newSecretUI(),
   938  		LoginUI:     &libkb.TestLoginUI{Username: u.username},
   939  	}
   940  	li := engine.NewLogin(u.tc.G, keybase1.DeviceTypeV2_DESKTOP, u.username, keybase1.ClientType_CLI)
   941  	mctx := libkb.NewMetaContextTODO(u.tc.G).WithUIs(uis)
   942  	err := engine.RunEngine2(mctx, li)
   943  	require.NoError(u.tc.T, err)
   944  }
   945  
   946  func (u *userPlusDevice) loginAfterReset() {
   947  	u.loginAfterResetHelper(true)
   948  }
   949  
   950  func (u *userPlusDevice) loginAfterResetPukless() {
   951  	u.loginAfterResetHelper(false)
   952  }
   953  
   954  func (u *userPlusDevice) loginAfterResetHelper(puk bool) {
   955  	t := u.device.tctx.T
   956  	u.device.tctx.Tp.DisableUpgradePerUserKey = !puk
   957  	g := u.device.tctx.G
   958  
   959  	// We have to reset a socket here, since we need to register
   960  	// the protocols in the genericUI below. If we reuse the previous
   961  	// socket, then the RPC protocols will not update, and we'll wind
   962  	// up reusing the old device name.
   963  	_, _, _, err := g.ResetSocket(true)
   964  	require.NoError(t, err)
   965  
   966  	devName := randomDevice()
   967  	g.Log.Debug("loginAfterResetHelper: new device name is %q", devName)
   968  
   969  	ui := genericUI{
   970  		g:           g,
   971  		SecretUI:    signupInfoSecretUI{u.userInfo, u.tc.G.GetLog()},
   972  		LoginUI:     usernameLoginUI{u.username},
   973  		ProvisionUI: nullProvisionUI{devName},
   974  	}
   975  	g.SetUI(&ui)
   976  	loginCmd := client.NewCmdLoginRunner(g)
   977  	loginCmd.Username = u.username
   978  	err = loginCmd.Run()
   979  	require.NoError(t, err, "login after reset")
   980  }
   981  
   982  func TestTeamTesterMultipleResets(t *testing.T) {
   983  	tt := newTeamTester(t)
   984  	defer tt.cleanup()
   985  
   986  	ann := tt.addUser("ann")
   987  	t.Logf("Signed up ann (%s)", ann.username)
   988  
   989  	ann.reset()
   990  	ann.loginAfterReset()
   991  
   992  	t.Logf("Ann resets for first time, uv is %v", ann.userVersion())
   993  
   994  	ann.reset()
   995  	ann.loginAfterReset()
   996  	t.Logf("Ann reset twice, uv is %v", ann.userVersion())
   997  }
   998  
   999  func (u *userPlusDevice) perUserKeyUpgrade() {
  1000  	t := u.device.tctx.T
  1001  	u.device.tctx.Tp.DisableUpgradePerUserKey = false
  1002  	g := u.device.tctx.G
  1003  	arg := &engine.PerUserKeyUpgradeArgs{}
  1004  	eng := engine.NewPerUserKeyUpgrade(g, arg)
  1005  	uis := libkb.UIs{
  1006  		LogUI: u.tc.G.Log,
  1007  	}
  1008  	m := libkb.NewMetaContextTODO(g).WithUIs(uis)
  1009  	err := engine.RunEngine2(m, eng)
  1010  	require.NoError(t, err, "Run engine.NewPerUserKeyUpgrade")
  1011  }
  1012  
  1013  func (u *userPlusDevice) disableTOFUSearch() {
  1014  	mctx := u.MetaContext()
  1015  	arg := libkb.NewAPIArg("test/disable_tofu_search_for_uid")
  1016  	arg.SessionType = libkb.APISessionTypeREQUIRED
  1017  	_, err := u.tc.G.API.Post(mctx, arg)
  1018  	require.NoError(u.tc.T, err)
  1019  }
  1020  
  1021  func (u *userPlusDevice) MetaContext() libkb.MetaContext {
  1022  	return libkb.NewMetaContextForTest(*u.tc)
  1023  }
  1024  
  1025  func kickAutoresetd(g *libkb.GlobalContext, t libkb.TestingTB) {
  1026  	mctx := libkb.NewMetaContextTODO(g)
  1027  	_, err := g.API.Post(mctx, libkb.APIArg{
  1028  		Endpoint:    "test/accelerate_autoresetd",
  1029  		SessionType: libkb.APISessionTypeREQUIRED,
  1030  	})
  1031  	require.NoError(t, err)
  1032  }
  1033  
  1034  func kickTeamRekeyd(g *libkb.GlobalContext, t libkb.TestingTB) {
  1035  	const workTimeSec = 1 // team_rekeyd delay before retrying job if it wasn't finished.
  1036  	args := libkb.HTTPArgs{
  1037  		"work_time_sec": libkb.I{Val: workTimeSec},
  1038  	}
  1039  	apiArg := libkb.APIArg{
  1040  		Endpoint:    "test/accelerate_team_rekeyd",
  1041  		Args:        args,
  1042  		SessionType: libkb.APISessionTypeREQUIRED,
  1043  	}
  1044  
  1045  	t.Logf("Calling accelerate_team_rekeyd, setting work_time_sec to %d", workTimeSec)
  1046  
  1047  	mctx := libkb.NewMetaContextTODO(g)
  1048  	_, err := g.API.Post(mctx, apiArg)
  1049  	require.NoError(t, err)
  1050  }
  1051  
  1052  func enableOpenSweepForTeam(g *libkb.GlobalContext, t libkb.TestingTB, teamID keybase1.TeamID) {
  1053  	args := libkb.HTTPArgs{
  1054  		"team_id": libkb.S{Val: teamID.String()},
  1055  	}
  1056  	apiArg := libkb.APIArg{
  1057  		Endpoint:    "test/team_enable_open_sweep",
  1058  		Args:        args,
  1059  		SessionType: libkb.APISessionTypeREQUIRED,
  1060  	}
  1061  
  1062  	t.Logf("Calling team_enable_open_sweep for team ID: %s", teamID)
  1063  
  1064  	_, err := g.API.Post(libkb.NewMetaContextTODO(g), apiArg)
  1065  	require.NoError(t, err)
  1066  }
  1067  
  1068  func GetTeamForTestByStringName(ctx context.Context, g *libkb.GlobalContext, name string) (*teams.Team, error) {
  1069  	return teams.Load(ctx, g, keybase1.LoadTeamArg{
  1070  		Name:        name,
  1071  		ForceRepoll: true,
  1072  	})
  1073  }
  1074  
  1075  func GetTeamForTestByID(ctx context.Context, g *libkb.GlobalContext, id keybase1.TeamID, public bool) (*teams.Team, error) {
  1076  	return teams.Load(ctx, g, keybase1.LoadTeamArg{
  1077  		ID:          id,
  1078  		Public:      public,
  1079  		ForceRepoll: true,
  1080  	})
  1081  }
  1082  
  1083  type teamNotifyHandler struct {
  1084  	changeCh                     chan keybase1.TeamChangedByIDArg
  1085  	abandonCh                    chan keybase1.TeamID
  1086  	badgeCh                      chan keybase1.BadgeState
  1087  	newTeamEKCh                  chan keybase1.NewTeamEkArg
  1088  	newTeambotEKCh               chan keybase1.NewTeambotEkArg
  1089  	teambotEKNeededCh            chan keybase1.TeambotEkNeededArg
  1090  	newTeambotKeyCh              chan keybase1.NewTeambotKeyArg
  1091  	teambotKeyNeededCh           chan keybase1.TeambotKeyNeededArg
  1092  	newlyAddedToTeam             chan keybase1.TeamID
  1093  	teamRoleMapCh                chan keybase1.UserTeamVersion
  1094  	metadataUpdateCh             chan struct{}
  1095  	teamTreeMembershipsPartialCh chan keybase1.TeamTreeMembership
  1096  	teamTreeMembershipsDoneCh    chan keybase1.TeamTreeMembershipsDoneResult
  1097  }
  1098  
  1099  func newTeamNotifyHandler() *teamNotifyHandler {
  1100  	return &teamNotifyHandler{
  1101  		changeCh:                     make(chan keybase1.TeamChangedByIDArg, 10),
  1102  		abandonCh:                    make(chan keybase1.TeamID, 10),
  1103  		badgeCh:                      make(chan keybase1.BadgeState, 10),
  1104  		newTeamEKCh:                  make(chan keybase1.NewTeamEkArg, 10),
  1105  		newTeambotEKCh:               make(chan keybase1.NewTeambotEkArg, 10),
  1106  		teambotEKNeededCh:            make(chan keybase1.TeambotEkNeededArg, 10),
  1107  		newTeambotKeyCh:              make(chan keybase1.NewTeambotKeyArg, 10),
  1108  		teambotKeyNeededCh:           make(chan keybase1.TeambotKeyNeededArg, 10),
  1109  		newlyAddedToTeam:             make(chan keybase1.TeamID, 10),
  1110  		teamRoleMapCh:                make(chan keybase1.UserTeamVersion, 100),
  1111  		metadataUpdateCh:             make(chan struct{}, 10),
  1112  		teamTreeMembershipsPartialCh: make(chan keybase1.TeamTreeMembership, 10),
  1113  		teamTreeMembershipsDoneCh:    make(chan keybase1.TeamTreeMembershipsDoneResult, 10),
  1114  	}
  1115  }
  1116  
  1117  func (n *teamNotifyHandler) TeamChangedByID(ctx context.Context, arg keybase1.TeamChangedByIDArg) error {
  1118  	n.changeCh <- arg
  1119  	return nil
  1120  }
  1121  
  1122  func (n *teamNotifyHandler) TeamChangedByName(ctx context.Context, arg keybase1.TeamChangedByNameArg) error {
  1123  	return nil
  1124  }
  1125  
  1126  func (n *teamNotifyHandler) TeamDeleted(ctx context.Context, teamID keybase1.TeamID) error {
  1127  	return nil
  1128  }
  1129  
  1130  func (n *teamNotifyHandler) TeamExit(ctx context.Context, teamID keybase1.TeamID) error {
  1131  	return nil
  1132  }
  1133  
  1134  func (n *teamNotifyHandler) NewlyAddedToTeam(ctx context.Context, teamID keybase1.TeamID) error {
  1135  	n.newlyAddedToTeam <- teamID
  1136  	return nil
  1137  }
  1138  
  1139  func (n *teamNotifyHandler) TeamMetadataUpdate(ctx context.Context) error {
  1140  	n.metadataUpdateCh <- struct{}{}
  1141  	return nil
  1142  }
  1143  
  1144  func (n *teamNotifyHandler) TeamAbandoned(ctx context.Context, teamID keybase1.TeamID) error {
  1145  	n.abandonCh <- teamID
  1146  	return nil
  1147  }
  1148  
  1149  func (n *teamNotifyHandler) BadgeState(ctx context.Context, badgeState keybase1.BadgeState) error {
  1150  	n.badgeCh <- badgeState
  1151  	return nil
  1152  }
  1153  
  1154  func (n *teamNotifyHandler) NewTeamEk(ctx context.Context, arg keybase1.NewTeamEkArg) error {
  1155  	n.newTeamEKCh <- arg
  1156  	return nil
  1157  }
  1158  
  1159  func (n *teamNotifyHandler) NewTeambotEk(ctx context.Context, arg keybase1.NewTeambotEkArg) error {
  1160  	n.newTeambotEKCh <- arg
  1161  	return nil
  1162  }
  1163  
  1164  func (n *teamNotifyHandler) TeambotEkNeeded(ctx context.Context, arg keybase1.TeambotEkNeededArg) error {
  1165  	n.teambotEKNeededCh <- arg
  1166  	return nil
  1167  }
  1168  
  1169  func (n *teamNotifyHandler) NewTeambotKey(ctx context.Context, arg keybase1.NewTeambotKeyArg) error {
  1170  	n.newTeambotKeyCh <- arg
  1171  	return nil
  1172  }
  1173  
  1174  func (n *teamNotifyHandler) TeambotKeyNeeded(ctx context.Context, arg keybase1.TeambotKeyNeededArg) error {
  1175  	n.teambotKeyNeededCh <- arg
  1176  	return nil
  1177  }
  1178  
  1179  func (n *teamNotifyHandler) AvatarUpdated(ctx context.Context, arg keybase1.AvatarUpdatedArg) error {
  1180  	return nil
  1181  }
  1182  
  1183  func (n *teamNotifyHandler) TeamRoleMapChanged(ctx context.Context, version keybase1.UserTeamVersion) error {
  1184  	n.teamRoleMapCh <- version
  1185  	return nil
  1186  }
  1187  
  1188  func (n *teamNotifyHandler) TeamTreeMembershipsPartial(ctx context.Context,
  1189  	arg keybase1.TeamTreeMembership) error {
  1190  
  1191  	n.teamTreeMembershipsPartialCh <- arg
  1192  	return nil
  1193  }
  1194  
  1195  func (n *teamNotifyHandler) TeamTreeMembershipsDone(ctx context.Context,
  1196  	arg keybase1.TeamTreeMembershipsDoneResult) error {
  1197  
  1198  	n.teamTreeMembershipsDoneCh <- arg
  1199  	return nil
  1200  }
  1201  
  1202  func TestGetTeamRootID(t *testing.T) {
  1203  	tt := newTeamTester(t)
  1204  	defer tt.cleanup()
  1205  
  1206  	tt.addUser("onr")
  1207  
  1208  	t.Logf("create a team")
  1209  	parentName, err := keybase1.TeamNameFromString(tt.users[0].createTeam())
  1210  	require.NoError(t, err)
  1211  
  1212  	parentID := parentName.ToPrivateTeamID()
  1213  
  1214  	t.Logf("create a subteam")
  1215  	subteamID, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "mysubteam", parentName, keybase1.TeamRole_NONE /* addSelfAs */)
  1216  	require.NoError(t, err)
  1217  
  1218  	subteamName, err := parentName.Append("mysubteam")
  1219  	require.NoError(t, err)
  1220  
  1221  	t.Logf("create a sub-subteam")
  1222  	subteamID2, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "teamofsubs", subteamName, keybase1.TeamRole_NONE /* addSelfAs */)
  1223  	require.NoError(t, err)
  1224  
  1225  	getAndCompare := func(id keybase1.TeamID) {
  1226  		retID, err := teams.GetRootID(context.TODO(), tt.users[0].tc.G, id)
  1227  		require.NoError(t, err)
  1228  		require.Equal(t, parentID, retID)
  1229  	}
  1230  
  1231  	getAndCompare(*subteamID)
  1232  	getAndCompare(*subteamID2)
  1233  	getAndCompare(parentID)
  1234  }
  1235  
  1236  // Test that we can still load a valid link a signed by a now-revoked device.
  1237  func TestTeamSignedByRevokedDevice(t *testing.T) {
  1238  	tt := newTeamTester(t)
  1239  	defer tt.cleanup()
  1240  
  1241  	// the signer
  1242  	alice := tt.addUserWithPaper("alice")
  1243  
  1244  	// the loader
  1245  	bob := tt.addUser("bob")
  1246  
  1247  	teamID, teamName := alice.createTeam2()
  1248  	// Delay team sigs in the merkle queue to try to elicit a bad race. As a regression test for CORE-8233.
  1249  	alice.delayMerkleTeam(teamID)
  1250  	alice.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_ADMIN)
  1251  
  1252  	t.Logf("alice revokes the device used to sign team links")
  1253  	var revokedKID keybase1.KID
  1254  	{
  1255  		devices, _ := getActiveDevicesAndKeys(alice.tc, alice.username)
  1256  		var target *libkb.Device
  1257  		for _, device := range devices {
  1258  			if device.Type != keybase1.DeviceTypeV2_PAPER {
  1259  				target = device
  1260  			}
  1261  		}
  1262  		require.NotNil(t, target)
  1263  		revokedKID = target.Kid
  1264  
  1265  		revokeAttemptsMax := 3
  1266  		var err error
  1267  		for i := 0; i < revokeAttemptsMax; i++ {
  1268  			t.Logf("revoke attempt %v / %v", i+1, revokeAttemptsMax)
  1269  			revokeEngine := engine.NewRevokeDeviceEngine(alice.tc.G, engine.RevokeDeviceEngineArgs{
  1270  				ID:        target.ID,
  1271  				ForceSelf: true,
  1272  				ForceLast: false,
  1273  			})
  1274  			uis := libkb.UIs{
  1275  				LogUI:    alice.tc.G.Log,
  1276  				SecretUI: alice.newSecretUI(),
  1277  			}
  1278  			m := libkb.NewMetaContextForTest(*alice.tc).WithUIs(uis)
  1279  			err = engine.RunEngine2(m, revokeEngine)
  1280  			if err == nil {
  1281  				break
  1282  			}
  1283  			t.Logf("revoke attempt %v failed: %v", i, err)
  1284  			if strings.Contains(err.Error(), "lazy merkle transaction in progress for key") {
  1285  				continue
  1286  			}
  1287  		}
  1288  		require.NoError(t, err)
  1289  	}
  1290  
  1291  	t.Logf("bob updates cache of alice's info")
  1292  	{
  1293  		arg := libkb.NewLoadUserArg(bob.tc.G).WithUID(alice.uid).WithPublicKeyOptional().WithForcePoll(true)
  1294  		_, _, err := bob.tc.G.GetUPAKLoader().LoadV2(arg)
  1295  		require.NoError(t, err)
  1296  	}
  1297  
  1298  	t.Logf("bob should see alice's key is revoked")
  1299  	{
  1300  		_, _, pubKey, err := bob.tc.G.GetUPAKLoader().LoadKeyV2(context.TODO(), alice.uid, revokedKID)
  1301  		require.NoError(t, err)
  1302  		t.Logf("%v", spew.Sdump(pubKey))
  1303  		require.NotNil(t, pubKey.Base.Revocation, "key should be revoked: %v", revokedKID)
  1304  	}
  1305  
  1306  	t.Logf("bob loads the team")
  1307  	_, err := teams.Load(context.TODO(), bob.tc.G, keybase1.LoadTeamArg{
  1308  		Name:            teamName.String(),
  1309  		ForceRepoll:     true,
  1310  		ForceFullReload: true, // don't use the cache
  1311  	})
  1312  	require.NoError(t, err)
  1313  }
  1314  
  1315  // Another test of loading a team with a valid link signed by a now-revoked
  1316  // device.  The previous test didn't catch a bug.  In this test at the time
  1317  // when the device is revoked the team sigchain points to a link that was
  1318  // signed by a never-revoked device and is subsequent to the link signed by the
  1319  // revoked device.
  1320  func TestTeamSignedByRevokedDevice2(t *testing.T) {
  1321  	tt := newTeamTester(t)
  1322  	defer tt.cleanup()
  1323  
  1324  	// the signer
  1325  	alice := tt.addUserWithPaper("alice")
  1326  	aliced2, cleanup := alice.provisionNewDevice()
  1327  	defer cleanup()
  1328  
  1329  	// the loader
  1330  	bob := tt.addUser("bob")
  1331  
  1332  	teamName := alice.createTeam()
  1333  
  1334  	t.Logf("sign a link with the to-be-revoked device (aliced2)")
  1335  	{
  1336  		eng := client.NewCmdTeamAddMemberRunner(aliced2.tctx.G)
  1337  		eng.Team = teamName
  1338  		eng.Username = bob.username
  1339  		eng.Role = keybase1.TeamRole_ADMIN
  1340  		err := eng.Run()
  1341  		require.NoError(t, err)
  1342  	}
  1343  
  1344  	alice.changeTeamMember(teamName, bob.username, keybase1.TeamRole_ADMIN)
  1345  
  1346  	t.Logf("alice revokes a device used to sign team links (alice2)")
  1347  	revokedKID := aliced2.KID()
  1348  	require.True(t, revokedKID.Exists())
  1349  	{
  1350  		devices, _ := getActiveDevicesAndKeys(alice.tc, alice.username)
  1351  		var target *libkb.Device
  1352  		for _, device := range devices {
  1353  			t.Logf("scan device: ID:%v KID:%v", device.ID, device.Kid)
  1354  			if device.Kid.Equal(revokedKID) {
  1355  				target = device
  1356  			}
  1357  		}
  1358  		require.NotNil(t, target)
  1359  
  1360  		revokeEngine := engine.NewRevokeDeviceEngine(alice.tc.G, engine.RevokeDeviceEngineArgs{
  1361  			ID:        target.ID,
  1362  			ForceSelf: true,
  1363  			ForceLast: false,
  1364  		})
  1365  		uis := libkb.UIs{
  1366  			LogUI:    alice.tc.G.Log,
  1367  			SecretUI: alice.newSecretUI(),
  1368  		}
  1369  		m := libkb.NewMetaContextForTest(*alice.tc).WithUIs(uis)
  1370  		err := engine.RunEngine2(m, revokeEngine)
  1371  		require.NoError(t, err)
  1372  	}
  1373  
  1374  	t.Logf("bob updates cache of alice's info")
  1375  	{
  1376  		arg := libkb.NewLoadUserArg(bob.tc.G).WithUID(alice.uid).WithPublicKeyOptional().WithForcePoll(true)
  1377  		_, _, err := bob.tc.G.GetUPAKLoader().LoadV2(arg)
  1378  		require.NoError(t, err)
  1379  	}
  1380  
  1381  	t.Logf("bob should see alice's key is revoked")
  1382  	{
  1383  		_, _, pubKey, err := bob.tc.G.GetUPAKLoader().LoadKeyV2(context.TODO(), alice.uid, revokedKID)
  1384  		require.NoError(t, err)
  1385  		t.Logf("%v", spew.Sdump(pubKey))
  1386  		require.NotNil(t, pubKey.Base.Revocation, "key should be revoked: %v", revokedKID)
  1387  	}
  1388  
  1389  	t.Logf("bob loads the team")
  1390  	_, err := teams.Load(context.TODO(), bob.tc.G, keybase1.LoadTeamArg{
  1391  		Name:            teamName,
  1392  		ForceRepoll:     true,
  1393  		ForceFullReload: true, // don't use the cache
  1394  	})
  1395  	require.NoError(t, err)
  1396  }
  1397  
  1398  func TestImpTeamLookupWithTrackingFailure(t *testing.T) {
  1399  	tt := newTeamTester(t)
  1400  	defer tt.cleanup()
  1401  
  1402  	alice := tt.addUser("alice")
  1403  	g := tt.users[0].tc.G
  1404  
  1405  	tt.addUser("wong")
  1406  	wong := tt.users[1]
  1407  
  1408  	iTeamNameCreate := strings.Join([]string{alice.username, wong.username}, ",")
  1409  
  1410  	t.Logf("make an implicit team")
  1411  	team, err := alice.lookupImplicitTeam(true /*create*/, iTeamNameCreate, false /*isPublic*/)
  1412  	require.NoError(t, err)
  1413  
  1414  	iui := newSimpleIdentifyUI()
  1415  	attachIdentifyUI(t, g, iui)
  1416  
  1417  	t.Logf("prove rooter and track")
  1418  	g.ProofCache.DisableDisk()
  1419  	wong.proveRooter()
  1420  	iui.confirmRes = keybase1.ConfirmResult{IdentityConfirmed: true, RemoteConfirmed: true, AutoConfirmed: true}
  1421  	tt.users[0].track(wong.username)
  1422  	iui.confirmRes = keybase1.ConfirmResult{}
  1423  
  1424  	t.Logf("make rooter unreachable")
  1425  	g.XAPI = &flakeyRooterAPI{orig: g.XAPI, hardFail: true, G: g}
  1426  	err = g.ProofCache.Reset()
  1427  	require.NoError(t, err)
  1428  
  1429  	t.Logf("lookup the implicit team while full identify is failing")
  1430  	team2, err := alice.lookupImplicitTeam(true /*create*/, iTeamNameCreate, false /*isPublic*/)
  1431  	require.NoError(t, err)
  1432  	require.Equal(t, team, team2)
  1433  }
  1434  
  1435  // Leave a team and make sure the team list no longer includes it.
  1436  func TestTeamLeaveThenList(t *testing.T) {
  1437  	tt := newTeamTester(t)
  1438  	defer tt.cleanup()
  1439  
  1440  	alice := tt.addUser("alice")
  1441  	bob := tt.addUser("bob")
  1442  
  1443  	teamID, teamName := alice.createTeam2()
  1444  	// add bob as owner because we can't leave as the only owner.
  1445  	alice.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_OWNER)
  1446  
  1447  	teams := alice.teamList("", false, false)
  1448  	require.Len(t, teams.Teams, 1)
  1449  	require.Equal(t, teamID, teams.Teams[0].TeamID)
  1450  
  1451  	alice.leave(teamName.String())
  1452  
  1453  	teams = alice.teamList("", false, false)
  1454  	require.Len(t, teams.Teams, 0)
  1455  }
  1456  
  1457  func TestTeamCanUserPerform(t *testing.T) {
  1458  	tt := newTeamTester(t)
  1459  	defer tt.cleanup()
  1460  
  1461  	ann := tt.addUser("ann")
  1462  	bob := tt.addUser("bob")
  1463  	pam := tt.addUser("pam")
  1464  	edd := tt.addUser("edd")
  1465  	jon := tt.addUser("jon")
  1466  
  1467  	team := ann.createTeam()
  1468  	ann.addTeamMember(team, bob.username, keybase1.TeamRole_ADMIN)
  1469  	ann.addTeamMember(team, pam.username, keybase1.TeamRole_WRITER)
  1470  	ann.addTeamMember(team, edd.username, keybase1.TeamRole_READER)
  1471  	ann.addTeamMember(team, jon.username, keybase1.TeamRole_ADMIN)
  1472  
  1473  	parentName, err := keybase1.TeamNameFromString(team)
  1474  	require.NoError(t, err)
  1475  
  1476  	_, err = teams.CreateSubteam(context.TODO(), ann.tc.G, "mysubteam", parentName, keybase1.TeamRole_NONE /* addSelfAs */)
  1477  	require.NoError(t, err)
  1478  	subteam := team + ".mysubteam"
  1479  	ann.addTeamMember(subteam, jon.username, keybase1.TeamRole_READER)
  1480  
  1481  	callCanPerform := func(user *userPlusDevice, teamname string) keybase1.TeamOperation {
  1482  		ret, err := teams.CanUserPerform(context.TODO(), user.tc.G, teamname)
  1483  		t.Logf("teams.CanUserPerform(%s,%s)", user.username, teamname)
  1484  		require.NoError(t, err)
  1485  		return ret
  1486  	}
  1487  	annPerms := callCanPerform(ann, team)
  1488  	bobPerms := callCanPerform(bob, team)
  1489  	pamPerms := callCanPerform(pam, team)
  1490  	eddPerms := callCanPerform(edd, team)
  1491  
  1492  	// All ops except leave and join should be fine for owners and admins
  1493  	require.True(t, annPerms.ManageMembers)
  1494  	require.True(t, annPerms.ManageSubteams)
  1495  	require.True(t, annPerms.CreateChannel)
  1496  	require.True(t, annPerms.DeleteChannel)
  1497  	require.True(t, annPerms.RenameChannel)
  1498  	require.True(t, annPerms.EditChannelDescription)
  1499  	require.True(t, annPerms.EditTeamDescription)
  1500  	require.True(t, annPerms.SetTeamShowcase)
  1501  	require.True(t, annPerms.SetMemberShowcase)
  1502  	require.True(t, annPerms.SetRetentionPolicy)
  1503  	require.True(t, annPerms.SetMinWriterRole)
  1504  	require.True(t, annPerms.ChangeOpenTeam)
  1505  	require.False(t, annPerms.LeaveTeam) // sole owner can't leave
  1506  	require.False(t, annPerms.ListFirst) // only true for implicit admins
  1507  	require.False(t, annPerms.JoinTeam)
  1508  	require.True(t, annPerms.SetPublicityAny)
  1509  	require.True(t, annPerms.ChangeTarsDisabled)
  1510  	require.True(t, annPerms.DeleteChatHistory)
  1511  	require.True(t, annPerms.Chat)
  1512  	require.True(t, annPerms.DeleteTeam)
  1513  
  1514  	require.True(t, bobPerms.ManageMembers)
  1515  	require.True(t, bobPerms.ManageSubteams)
  1516  	require.True(t, bobPerms.CreateChannel)
  1517  	require.True(t, bobPerms.DeleteChannel)
  1518  	require.True(t, bobPerms.RenameChannel)
  1519  	require.True(t, bobPerms.EditChannelDescription)
  1520  	require.True(t, bobPerms.EditTeamDescription)
  1521  	require.True(t, bobPerms.SetTeamShowcase)
  1522  	require.True(t, bobPerms.SetMemberShowcase)
  1523  	require.True(t, bobPerms.SetRetentionPolicy)
  1524  	require.True(t, bobPerms.SetMinWriterRole)
  1525  	require.True(t, bobPerms.ChangeOpenTeam)
  1526  	require.True(t, bobPerms.LeaveTeam)
  1527  	require.False(t, bobPerms.ListFirst)
  1528  	require.False(t, bobPerms.JoinTeam)
  1529  	require.True(t, bobPerms.SetPublicityAny)
  1530  	require.True(t, bobPerms.ChangeTarsDisabled)
  1531  	require.True(t, bobPerms.DeleteChatHistory)
  1532  	require.True(t, bobPerms.Chat)
  1533  	require.False(t, bobPerms.DeleteTeam)
  1534  
  1535  	// Some ops are fine for writers
  1536  	require.False(t, pamPerms.ManageMembers)
  1537  	require.False(t, pamPerms.ManageSubteams)
  1538  	require.True(t, pamPerms.CreateChannel)
  1539  	require.False(t, pamPerms.DeleteChannel)
  1540  	require.True(t, pamPerms.RenameChannel)
  1541  	require.True(t, pamPerms.EditChannelDescription)
  1542  	require.False(t, pamPerms.EditTeamDescription)
  1543  	require.False(t, pamPerms.SetTeamShowcase)
  1544  	require.True(t, pamPerms.SetMemberShowcase)
  1545  	require.False(t, pamPerms.SetRetentionPolicy)
  1546  	require.False(t, pamPerms.SetMinWriterRole)
  1547  	require.False(t, pamPerms.ChangeOpenTeam)
  1548  	require.True(t, pamPerms.LeaveTeam)
  1549  	require.False(t, pamPerms.ListFirst)
  1550  	require.False(t, pamPerms.JoinTeam)
  1551  	require.False(t, pamPerms.SetPublicityAny)
  1552  	require.False(t, pamPerms.ChangeTarsDisabled)
  1553  	require.False(t, pamPerms.DeleteChatHistory)
  1554  	require.True(t, pamPerms.Chat)
  1555  	require.False(t, pamPerms.DeleteTeam)
  1556  
  1557  	// Only SetMemberShowcase (by default), LeaveTeam, and Chat is available for readers
  1558  	require.False(t, eddPerms.ManageMembers)
  1559  	require.False(t, eddPerms.ManageSubteams)
  1560  	require.False(t, eddPerms.CreateChannel)
  1561  	require.False(t, eddPerms.DeleteChannel)
  1562  	require.False(t, eddPerms.RenameChannel)
  1563  	require.False(t, eddPerms.EditChannelDescription)
  1564  	require.False(t, eddPerms.EditTeamDescription)
  1565  	require.False(t, eddPerms.SetTeamShowcase)
  1566  	require.True(t, eddPerms.SetMemberShowcase)
  1567  	require.False(t, eddPerms.SetRetentionPolicy)
  1568  	require.False(t, eddPerms.SetMinWriterRole)
  1569  	require.False(t, eddPerms.ChangeOpenTeam)
  1570  	require.True(t, eddPerms.LeaveTeam)
  1571  	require.False(t, eddPerms.ListFirst)
  1572  	require.False(t, eddPerms.JoinTeam)
  1573  	require.False(t, eddPerms.SetPublicityAny)
  1574  	require.False(t, eddPerms.ChangeTarsDisabled)
  1575  	require.False(t, eddPerms.DeleteChatHistory)
  1576  	require.True(t, eddPerms.Chat)
  1577  	require.False(t, eddPerms.DeleteTeam)
  1578  
  1579  	annPerms = callCanPerform(ann, subteam)
  1580  	bobPerms = callCanPerform(bob, subteam)
  1581  	jonPerms := callCanPerform(jon, subteam)
  1582  
  1583  	// Some ops are fine for implicit admins
  1584  	require.True(t, annPerms.ManageMembers)
  1585  	require.True(t, annPerms.ManageSubteams)
  1586  	require.False(t, annPerms.CreateChannel)
  1587  	require.False(t, annPerms.DeleteChannel)
  1588  	require.False(t, annPerms.RenameChannel)
  1589  	require.False(t, annPerms.EditChannelDescription)
  1590  	require.True(t, annPerms.EditTeamDescription)
  1591  	require.True(t, annPerms.SetTeamShowcase)
  1592  	require.False(t, annPerms.SetMemberShowcase)
  1593  	require.False(t, annPerms.SetRetentionPolicy)
  1594  	require.False(t, annPerms.SetMinWriterRole)
  1595  	require.True(t, annPerms.ChangeOpenTeam) // not a member of the subteam
  1596  	require.True(t, annPerms.ListFirst)
  1597  	require.True(t, annPerms.JoinTeam)
  1598  	require.True(t, annPerms.SetPublicityAny)
  1599  	require.True(t, annPerms.ChangeTarsDisabled)
  1600  	require.False(t, annPerms.DeleteChatHistory)
  1601  	require.False(t, annPerms.Chat)
  1602  	require.True(t, annPerms.DeleteTeam)
  1603  
  1604  	require.True(t, bobPerms.ManageMembers)
  1605  	require.True(t, bobPerms.ManageSubteams)
  1606  	require.False(t, bobPerms.CreateChannel)
  1607  	require.False(t, bobPerms.DeleteChannel)
  1608  	require.False(t, bobPerms.RenameChannel)
  1609  	require.False(t, bobPerms.EditChannelDescription)
  1610  	require.True(t, bobPerms.EditTeamDescription)
  1611  	require.True(t, bobPerms.SetTeamShowcase)
  1612  	require.False(t, bobPerms.SetMemberShowcase)
  1613  	require.False(t, bobPerms.SetRetentionPolicy)
  1614  	require.False(t, bobPerms.SetMinWriterRole)
  1615  	require.True(t, bobPerms.ChangeOpenTeam)
  1616  	require.False(t, bobPerms.LeaveTeam) // not a member of the subteam
  1617  	require.True(t, bobPerms.ListFirst)
  1618  	require.True(t, bobPerms.JoinTeam)
  1619  	require.True(t, bobPerms.SetPublicityAny)
  1620  	require.True(t, bobPerms.ChangeTarsDisabled)
  1621  	require.False(t, bobPerms.DeleteChatHistory)
  1622  	require.False(t, bobPerms.Chat)
  1623  	require.True(t, bobPerms.DeleteTeam)
  1624  
  1625  	// make sure JoinTeam is false since already a member
  1626  	require.True(t, jonPerms.ManageMembers)
  1627  	require.True(t, jonPerms.ManageSubteams)
  1628  	require.False(t, jonPerms.CreateChannel)
  1629  	require.False(t, jonPerms.DeleteChannel)
  1630  	require.False(t, jonPerms.RenameChannel)
  1631  	require.False(t, jonPerms.EditChannelDescription)
  1632  	require.True(t, jonPerms.EditTeamDescription)
  1633  	require.True(t, jonPerms.SetTeamShowcase)
  1634  	require.True(t, jonPerms.SetMemberShowcase)
  1635  	require.False(t, jonPerms.SetRetentionPolicy)
  1636  	require.False(t, jonPerms.SetMinWriterRole)
  1637  	require.True(t, jonPerms.ChangeOpenTeam)
  1638  	require.True(t, jonPerms.LeaveTeam)
  1639  	require.True(t, jonPerms.ListFirst)
  1640  	require.False(t, jonPerms.JoinTeam)
  1641  	require.True(t, jonPerms.SetPublicityAny)
  1642  	require.True(t, jonPerms.ChangeTarsDisabled)
  1643  	require.False(t, jonPerms.DeleteChatHistory)
  1644  	require.True(t, jonPerms.Chat)
  1645  	require.True(t, jonPerms.DeleteTeam)
  1646  
  1647  	// Invalid team for pam, no error
  1648  	_, err = teams.CanUserPerform(context.TODO(), pam.tc.G, subteam)
  1649  	require.NoError(t, err)
  1650  
  1651  	// Non-membership shouldn't be an error
  1652  	donny := tt.addUser("donny")
  1653  	donnyPerms, err := teams.CanUserPerform(context.TODO(), donny.tc.G, team)
  1654  	require.NoError(t, err, "non-member canUserPerform")
  1655  
  1656  	// Make sure a non-member can't do stuff
  1657  	require.False(t, donnyPerms.ManageMembers)
  1658  	require.False(t, donnyPerms.ManageSubteams)
  1659  	require.False(t, donnyPerms.CreateChannel)
  1660  	require.False(t, donnyPerms.DeleteChannel)
  1661  	require.False(t, donnyPerms.RenameChannel)
  1662  	require.False(t, donnyPerms.EditChannelDescription)
  1663  	require.False(t, donnyPerms.EditTeamDescription)
  1664  	require.False(t, donnyPerms.SetTeamShowcase)
  1665  	require.False(t, donnyPerms.SetMemberShowcase)
  1666  	require.False(t, donnyPerms.SetRetentionPolicy)
  1667  	require.False(t, donnyPerms.SetMinWriterRole)
  1668  	require.False(t, donnyPerms.ChangeOpenTeam)
  1669  	require.False(t, donnyPerms.ListFirst)
  1670  	// TBD: require.True(t, donnyPerms.JoinTeam)
  1671  	require.False(t, donnyPerms.SetPublicityAny)
  1672  	require.False(t, donnyPerms.DeleteChatHistory)
  1673  	require.False(t, donnyPerms.Chat)
  1674  	require.False(t, donnyPerms.DeleteTeam)
  1675  }
  1676  
  1677  func TestBatchAddMembersCLI(t *testing.T) {
  1678  	tt := newTeamTester(t)
  1679  	defer tt.cleanup()
  1680  
  1681  	alice := tt.addUser("alice")
  1682  	bob := tt.addUser("bob")
  1683  	ciara := tt.addUser("ciara")
  1684  	dodo := tt.addUser("dodo")
  1685  	botua := tt.addUser("botua")
  1686  	restrictedBotua := tt.addUser("rbot")
  1687  	john := tt.addPuklessUser("john")
  1688  	tt.logUserNames()
  1689  	teamID, _ := alice.createTeam2()
  1690  
  1691  	handler := service.NewAccountHandler(nil, ciara.tc.G)
  1692  
  1693  	// ciara doesn't allow alice to add them to team
  1694  	err := handler.UserSetContactSettings(context.Background(), keybase1.ContactSettings{
  1695  		Enabled:              true,
  1696  		AllowFolloweeDegrees: 0,
  1697  	})
  1698  	require.NoError(t, err)
  1699  
  1700  	dodo.proveRooter()
  1701  	users := []keybase1.UserRolePair{
  1702  		{Assertion: bob.username, Role: keybase1.TeamRole_ADMIN},
  1703  		{Assertion: ciara.username, Role: keybase1.TeamRole_WRITER},
  1704  		{Assertion: dodo.username + "+" + dodo.username + "@rooter", Role: keybase1.TeamRole_WRITER},
  1705  		{Assertion: john.username + "@rooter", Role: keybase1.TeamRole_ADMIN},
  1706  		{Assertion: john.username + "@keybase", Role: keybase1.TeamRole_READER},
  1707  		{Assertion: "[rob@gmail.com]@email", Role: keybase1.TeamRole_READER},
  1708  		{Assertion: botua.username, Role: keybase1.TeamRole_BOT},
  1709  		{Assertion: restrictedBotua.username, Role: keybase1.TeamRole_RESTRICTEDBOT, BotSettings: &keybase1.TeamBotSettings{}},
  1710  	}
  1711  	added, notAdded, err := teams.AddMembers(context.Background(), alice.tc.G, teamID, users, nil /* emailInviteMsg */)
  1712  	require.NoError(t, err)
  1713  	require.Len(t, added, 7)
  1714  	require.Len(t, notAdded, 1)
  1715  	require.Equal(t, keybase1.User{
  1716  		Uid:      ciara.uid,
  1717  		Username: ciara.username,
  1718  	}, notAdded[0])
  1719  
  1720  	team := alice.loadTeamByID(teamID, true /* admin */)
  1721  	members, err := team.Members()
  1722  	require.NoError(t, err)
  1723  	require.Equal(t, []keybase1.UserVersion{{Uid: alice.uid, EldestSeqno: 1}}, members.Owners)
  1724  	require.Equal(t, []keybase1.UserVersion{{Uid: bob.uid, EldestSeqno: 1}}, members.Admins)
  1725  	require.Equal(t, []keybase1.UserVersion{{Uid: dodo.uid, EldestSeqno: 1}}, members.Writers)
  1726  	require.Len(t, members.Readers, 0)
  1727  	require.Equal(t, []keybase1.UserVersion{{Uid: botua.uid, EldestSeqno: 1}}, members.Bots)
  1728  	require.Equal(t, []keybase1.UserVersion{{Uid: restrictedBotua.uid, EldestSeqno: 1}}, members.RestrictedBots)
  1729  	invites := team.GetActiveAndObsoleteInvites()
  1730  	t.Logf("invites: %s", spew.Sdump(invites))
  1731  	for _, invite := range invites {
  1732  		switch invite.Type.C__ {
  1733  		case keybase1.TeamInviteCategory_SBS:
  1734  			require.Equal(t, invite.Type.Sbs(), keybase1.TeamInviteSocialNetwork("rooter"))
  1735  			require.Equal(t, invite.Name, keybase1.TeamInviteName(john.username))
  1736  			require.Equal(t, invite.Role, keybase1.TeamRole_ADMIN)
  1737  		case keybase1.TeamInviteCategory_EMAIL:
  1738  			require.Equal(t, invite.Name, keybase1.TeamInviteName("rob@gmail.com"))
  1739  			require.Equal(t, invite.Role, keybase1.TeamRole_READER)
  1740  		case keybase1.TeamInviteCategory_KEYBASE:
  1741  			require.Equal(t, invite.Name, keybase1.TeamInviteName(john.userVersion().PercentForm()))
  1742  			require.Equal(t, invite.Role, keybase1.TeamRole_READER)
  1743  		default:
  1744  			require.FailNowf(t, "unexpected invite type", "%v", spew.Sdump(invite))
  1745  		}
  1746  	}
  1747  
  1748  	// It should fail to combine assertions with email addresses
  1749  	users = []keybase1.UserRolePair{
  1750  		{Assertion: "[job@gmail.com]@email+job33", Role: keybase1.TeamRole_READER},
  1751  	}
  1752  	_, _, err = teams.AddMembers(context.Background(), alice.tc.G, teamID, users, nil /* emailInviteMsg */)
  1753  	require.Error(t, err)
  1754  	require.IsType(t, err, teams.AddMembersError{})
  1755  	require.IsType(t, err.(teams.AddMembersError).Err, teams.MixedServerTrustAssertionError{})
  1756  	// It should also fail to combine invites with other assertions
  1757  	users = []keybase1.UserRolePair{
  1758  		{Assertion: "xxffee22ee@twitter+jjjejiei3i@rooter", Role: keybase1.TeamRole_READER},
  1759  	}
  1760  	_, _, err = teams.AddMembers(context.Background(), alice.tc.G, teamID, users, nil /* emailInviteMsg */)
  1761  	require.Error(t, err)
  1762  	require.IsType(t, err, teams.AddMembersError{})
  1763  	require.IsType(t, err.(teams.AddMembersError).Err, teams.CompoundInviteError{})
  1764  }
  1765  
  1766  func TestBatchAddMembers(t *testing.T) {
  1767  	tt := newTeamTester(t)
  1768  	defer tt.cleanup()
  1769  
  1770  	alice := tt.addUser("alice")
  1771  	bob := tt.addUser("bob")
  1772  	john := tt.addPuklessUser("john")
  1773  	rob := tt.addPuklessUser("rob")
  1774  	tt.logUserNames()
  1775  
  1776  	teamID, _ := alice.createTeam2()
  1777  
  1778  	assertions := []string{
  1779  		bob.username,
  1780  		john.username,
  1781  		rob.username,
  1782  		bob.username + "@rooter",
  1783  		"foodle@twitter",
  1784  	}
  1785  	expectInvite := []bool{false, true, true, true, true}
  1786  	expectUsername := []bool{true, true, true, false, false}
  1787  	role := keybase1.TeamRole_OWNER
  1788  
  1789  	makeUserRolePairs := func(v []string, role keybase1.TeamRole) []keybase1.UserRolePair {
  1790  		var ret []keybase1.UserRolePair
  1791  		for _, s := range v {
  1792  			ret = append(ret, keybase1.UserRolePair{Assertion: s, Role: role})
  1793  		}
  1794  		return ret
  1795  	}
  1796  
  1797  	added, notAdded, err := teams.AddMembers(context.Background(), alice.tc.G, teamID, makeUserRolePairs(assertions, role), nil /* emailInviteMsg */)
  1798  	require.Error(t, err, "can't invite assertions as owners")
  1799  	require.IsType(t, teams.AttemptedInviteSocialOwnerError{}, err)
  1800  	require.Nil(t, added)
  1801  	require.Nil(t, notAdded)
  1802  	team := alice.loadTeamByID(teamID, true /* admin */)
  1803  	members, err := team.Members()
  1804  	require.NoError(t, err)
  1805  	require.Len(t, members.Owners, 1)
  1806  	require.Len(t, members.Admins, 0)
  1807  	require.Len(t, members.Writers, 0)
  1808  	require.Len(t, members.Readers, 0)
  1809  	require.Len(t, members.RestrictedBots, 0)
  1810  
  1811  	role = keybase1.TeamRole_ADMIN
  1812  	added, notAdded, err = teams.AddMembers(context.Background(), alice.tc.G, teamID, makeUserRolePairs(assertions, role), nil /* emailInviteMsg */)
  1813  	require.NoError(t, err)
  1814  	require.Len(t, added, len(assertions))
  1815  	require.Len(t, notAdded, 0)
  1816  	for i, r := range added {
  1817  		require.Equal(t, expectInvite[i], r.Invite, "invite %v", i)
  1818  		if expectUsername[i] {
  1819  			require.Equal(t, assertions[i], r.Username.String(), "expected username %v", i)
  1820  		} else {
  1821  			require.Equal(t, "", r.Username.String(), "expected no username %v", i)
  1822  		}
  1823  	}
  1824  
  1825  	team = alice.loadTeamByID(teamID, true /* admin */)
  1826  	members, err = team.Members()
  1827  	require.NoError(t, err)
  1828  	require.Len(t, members.Owners, 1)
  1829  	require.Equal(t, alice.userVersion(), members.Owners[0])
  1830  	require.Len(t, members.Admins, 1)
  1831  	require.Equal(t, bob.userVersion(), members.Admins[0])
  1832  	require.Len(t, members.Writers, 0)
  1833  	require.Len(t, members.Readers, 0)
  1834  	require.Len(t, members.RestrictedBots, 0)
  1835  
  1836  	invites := team.GetActiveAndObsoleteInvites()
  1837  	t.Logf("invites: %s", spew.Sdump(invites))
  1838  	sbsCount := 0
  1839  	expectInvites := make(map[string]struct{})
  1840  	expectInvites[john.userVersion().String()] = struct{}{}
  1841  	expectInvites[rob.userVersion().String()] = struct{}{}
  1842  	for x, invite := range invites {
  1843  		t.Logf("invites[%v]", x)
  1844  		require.Equal(t, invite.Role, role)
  1845  		switch invite.Type.C__ {
  1846  		case keybase1.TeamInviteCategory_SBS:
  1847  			switch invite.Type.Sbs() {
  1848  			case "twitter":
  1849  				require.Equal(t, "foodle", string(invite.Name))
  1850  			case "rooter":
  1851  				require.Equal(t, bob.username, string(invite.Name))
  1852  			default:
  1853  				require.FailNowf(t, "unexpected invite service", "%v", spew.Sdump(invite))
  1854  			}
  1855  			sbsCount++
  1856  		case keybase1.TeamInviteCategory_KEYBASE:
  1857  			require.Contains(t, expectInvites, string(invite.Name))
  1858  			delete(expectInvites, string(invite.Name))
  1859  		default:
  1860  			require.FailNowf(t, "unexpected invite type", "%v", spew.Sdump(invite))
  1861  		}
  1862  	}
  1863  	require.Equal(t, 2, sbsCount, "sbs count")
  1864  }
  1865  
  1866  func TestAddCompoundAssertion(t *testing.T) {
  1867  	tt := newTeamTester(t)
  1868  	defer tt.cleanup()
  1869  
  1870  	alice := tt.addUser("alice")
  1871  	bob := tt.addUser("bob")
  1872  
  1873  	teamID, _ := alice.createTeam2()
  1874  
  1875  	bob.proveRooter()
  1876  
  1877  	assertion := fmt.Sprintf("%s@uid+%s@rooter", bob.uid, bob.username)
  1878  
  1879  	users := []keybase1.UserRolePair{
  1880  		{Assertion: assertion, Role: keybase1.TeamRole_WRITER},
  1881  	}
  1882  	added, notAdded, err := teams.AddMembers(context.Background(), alice.tc.G, teamID, users, nil /* emailInviteMsg */)
  1883  	require.NoError(t, err)
  1884  	require.Len(t, notAdded, 0)
  1885  	require.Len(t, added, 1)
  1886  	require.False(t, added[0].Invite)
  1887  	require.EqualValues(t, bob.username, added[0].Username)
  1888  }
  1889  
  1890  func TestTeamBustResolverCacheOnSubteamRename(t *testing.T) {
  1891  	tt := newTeamTester(t)
  1892  	defer tt.cleanup()
  1893  
  1894  	al := tt.addUser("al")
  1895  	bob := tt.addUser("bob")
  1896  	eve := tt.addUser("eve")
  1897  
  1898  	_, teamName := al.createTeam2()
  1899  
  1900  	// Verify subteams that have been renamed resolve correctly
  1901  	subteamBasename := "bb1"
  1902  	subteamID, err := teams.CreateSubteam(context.TODO(), al.tc.G, subteamBasename, teamName, keybase1.TeamRole_NONE /* addSelfAs */)
  1903  	require.NoError(t, err)
  1904  	subteamName, err := teamName.Append(subteamBasename)
  1905  	require.NoError(t, err)
  1906  
  1907  	al.addTeamMember(subteamName.String(), bob.username, keybase1.TeamRole_READER)
  1908  	al.addTeamMember(teamName.String(), eve.username, keybase1.TeamRole_ADMIN)
  1909  
  1910  	subteamRename, err := teamName.Append("bb2")
  1911  	require.NoError(t, err)
  1912  
  1913  	subteamNameActual, err := teams.ResolveIDToName(context.TODO(), al.tc.G, *subteamID)
  1914  	require.NoError(t, err)
  1915  	require.True(t, subteamName.Eq(subteamNameActual))
  1916  
  1917  	err = teams.RenameSubteam(context.TODO(), al.tc.G, subteamName, subteamRename)
  1918  	require.NoError(t, err)
  1919  
  1920  	// While this may not be ideal, admin that posts the rename will
  1921  	// get two notifications.
  1922  	// - First notification comes from `RenameSubteam` func itself,
  1923  	//   where `g.GetTeamLoader().NotifyTeamRename` is called.
  1924  	// - Second one is the regular gregor team.rename notification.
  1925  	t.Logf("Waiting for team notifications for %s", al.username)
  1926  	al.waitForTeamChangeRenamed(*subteamID)
  1927  	al.waitForTeamChangeRenamed(*subteamID)
  1928  
  1929  	// Members of subteam, and other admins from parent teams, will
  1930  	// get just one.
  1931  	for _, user := range []*userPlusDevice{bob, eve} {
  1932  		t.Logf("Waiting for team notifications for %s", user.username)
  1933  		user.waitForTeamChangeRenamed(*subteamID)
  1934  	}
  1935  
  1936  	for _, user := range tt.users {
  1937  		subteamRenameActual, err := teams.ResolveIDToName(context.TODO(), user.tc.G, *subteamID)
  1938  		require.NoError(t, err)
  1939  		require.True(t, subteamRename.Eq(subteamRenameActual))
  1940  
  1941  		_, err = teams.ResolveNameToID(context.TODO(), user.tc.G, subteamName)
  1942  		require.Error(t, err)
  1943  	}
  1944  }
  1945  
  1946  func TestForceRepollState(t *testing.T) {
  1947  	tt := newTeamTester(t)
  1948  	defer tt.cleanup()
  1949  
  1950  	tt.addUser("onr")
  1951  	tt.addUser("wtr")
  1952  
  1953  	mctx := libkb.NewMetaContextForTest(*tt.users[0].tc)
  1954  	_, err := mctx.G().API.Post(mctx, libkb.APIArg{
  1955  		Endpoint:    "test/big_state_cutoff",
  1956  		SessionType: libkb.APISessionTypeREQUIRED,
  1957  		Args: libkb.HTTPArgs{
  1958  			"cutoff": libkb.I{Val: 1},
  1959  		},
  1960  	})
  1961  	require.NoError(t, err)
  1962  
  1963  	team := tt.users[0].createTeam()
  1964  	for i := 0; i < 3; i++ {
  1965  		tt.users[0].addTeamMember(team, tt.users[1].username, keybase1.TeamRole_WRITER)
  1966  		tt.users[0].removeTeamMember(team, tt.users[1].username)
  1967  	}
  1968  	found := false
  1969  	w := 10 * time.Millisecond
  1970  	for i := 0; i < 10; i++ {
  1971  		found = tt.users[0].tc.G.GetTeamLoader().(*teams.TeamLoader).InForceRepollMode(mctx)
  1972  		if found {
  1973  			break
  1974  		}
  1975  		w *= 2
  1976  		t.Logf("Waiting for %v, for gregor state update", w)
  1977  		time.Sleep(w)
  1978  	}
  1979  	require.True(t, found)
  1980  }
  1981  
  1982  func TestTeamMetadataUpdateNotifications(t *testing.T) {
  1983  	tt := newTeamTester(t)
  1984  	defer tt.cleanup()
  1985  
  1986  	tt.addUser("alf")
  1987  	tt.addUser("bra")
  1988  	tt.addUser("cha")
  1989  
  1990  	parentID, parentName := tt.users[0].createTeam2()
  1991  	_, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "bb", parentName, keybase1.TeamRole_NONE /* addSelfAs */)
  1992  	require.NoError(t, err)
  1993  	subteamName, err := parentName.Append("bb")
  1994  	require.NoError(t, err)
  1995  	subteamID, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "cc", subteamName, keybase1.TeamRole_NONE /* addSelfAs */)
  1996  	require.NoError(t, err)
  1997  	subsubteamName, err := subteamName.Append("cc")
  1998  	require.NoError(t, err)
  1999  
  2000  	t.Logf("Start testing metadata updates")
  2001  
  2002  	tt.users[0].addTeamMember(subsubteamName.String(), tt.users[2].username, keybase1.TeamRole_ADMIN)
  2003  	tt.users[2].waitForMetadataUpdateGregor("added to team")
  2004  
  2005  	tt.users[0].addTeamMember(parentName.String(), tt.users[1].username, keybase1.TeamRole_ADMIN)
  2006  	tt.users[1].waitForMetadataUpdateGregor("added to team")
  2007  
  2008  	subsubteamRename, err := subteamName.Append("cc2")
  2009  	require.NoError(t, err)
  2010  	err = teams.RenameSubteam(context.TODO(), tt.users[0].tc.G, subsubteamName, subsubteamRename)
  2011  	require.NoError(t, err)
  2012  	tt.users[1].waitForMetadataUpdateGregor("team user was an implicit admin of changed name")
  2013  	tt.users[2].waitForMetadataUpdateGregor("team user was an implicit admin of changed name")
  2014  
  2015  	subteamRename, err := parentName.Append("bb2")
  2016  	require.NoError(t, err)
  2017  	err = teams.RenameSubteam(context.TODO(), tt.users[0].tc.G, subteamName, subteamRename)
  2018  	require.NoError(t, err)
  2019  	// Suboptimality - but it's fine since renames are rare.
  2020  	tt.users[1].waitForMetadataUpdateGregor("team user was an implicit admin of changed name (subteam)")
  2021  	tt.users[1].waitForMetadataUpdateGregor("team user was an implicit admin of changed name (subsubteam)")
  2022  	tt.users[2].waitForMetadataUpdateGregor("parent team of subteam you're in changed name")
  2023  
  2024  	tt.users[0].changeTeamMember(parentName.String(), tt.users[1].username, keybase1.TeamRole_OWNER)
  2025  	tt.users[1].waitForMetadataUpdateGregor("now an owner")
  2026  
  2027  	tt.users[0].teamSetSettings(*subteamID, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_READER})
  2028  	tt.users[1].waitForMetadataUpdateGregor("settings change of subteam")
  2029  
  2030  	_, err = subteamRename.Append("cc2")
  2031  	require.NoError(t, err)
  2032  	tt.users[0].teamSetSettings(*subteamID, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_WRITER})
  2033  	tt.users[1].waitForMetadataUpdateGregor("settings change of subsubteam")
  2034  	tt.users[0].teamSetSettings(*subteamID, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_READER})
  2035  	tt.users[1].waitForMetadataUpdateGregor("settings change of subsubteam")
  2036  
  2037  	val := true
  2038  	err = tt.users[0].teamsClient.SetTeamShowcase(context.Background(), keybase1.SetTeamShowcaseArg{
  2039  		TeamID:      *subteamID,
  2040  		IsShowcased: &val,
  2041  	})
  2042  	require.NoError(tt.users[0].tc.T, err)
  2043  	tt.users[1].waitForMetadataUpdateGregor("change showcase")
  2044  
  2045  	desc := "desc"
  2046  	err = tt.users[0].teamsClient.SetTeamShowcase(context.Background(), keybase1.SetTeamShowcaseArg{
  2047  		TeamID:      *subteamID,
  2048  		IsShowcased: &val,
  2049  		Description: &desc,
  2050  	})
  2051  	require.NoError(tt.users[0].tc.T, err)
  2052  	tt.users[1].waitForMetadataUpdateGregor("change showcase")
  2053  
  2054  	err = tt.users[0].teamsClient.SetTeamShowcase(context.Background(), keybase1.SetTeamShowcaseArg{
  2055  		TeamID:            *subteamID,
  2056  		IsShowcased:       &val,
  2057  		Description:       &desc,
  2058  		AnyMemberShowcase: &val,
  2059  	})
  2060  	require.NoError(tt.users[0].tc.T, err)
  2061  	tt.users[1].waitForMetadataUpdateGregor("change showcase")
  2062  
  2063  	newTeamID, newteamName := tt.users[1].createTeam2()
  2064  	require.NoError(t, err)
  2065  	tt.users[1].waitForMetadataUpdateGregor("new team")
  2066  	tt.users[1].addTeamMember(newteamName.String(), tt.users[0].username, keybase1.TeamRole_OWNER)
  2067  	tt.users[1].waitForMetadataUpdateGregor("added someone to team")
  2068  
  2069  	tui := &teamsUI{}
  2070  	err = teams.Delete(context.Background(), tt.users[0].tc.G, tui, newTeamID)
  2071  	require.NoError(tt.users[0].tc.T, err)
  2072  	tt.users[1].waitForMetadataUpdateGregor("team deleted")
  2073  
  2074  	err = tt.users[1].teamsClient.SetTeamMemberShowcase(context.Background(), keybase1.SetTeamMemberShowcaseArg{
  2075  		TeamID:      parentID,
  2076  		IsShowcased: true,
  2077  	})
  2078  	require.NoError(tt.users[1].tc.T, err)
  2079  	tt.users[1].waitForMetadataUpdateGregor("change member showcase")
  2080  
  2081  	tt.users[1].waitForMetadataUpdateGregor("change member count from TeamMemberCountCache")
  2082  }
  2083  
  2084  func TestTeamLoadParentAfterRotateRace(t *testing.T) {
  2085  	tt := newTeamTester(t)
  2086  	defer tt.cleanup()
  2087  
  2088  	tt.addUser("alf")
  2089  	tt.addUser("bra")
  2090  
  2091  	team := tt.users[0].createTeam()
  2092  	parentName, err := keybase1.TeamNameFromString(team)
  2093  	require.NoError(t, err)
  2094  	_, err = teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "bb", parentName, keybase1.TeamRole_NONE /* addSelfAs */)
  2095  	require.NoError(t, err)
  2096  	subteamName, err := parentName.Append("bb")
  2097  	require.NoError(t, err)
  2098  	_, err = teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "cc", subteamName, keybase1.TeamRole_NONE /* addSelfAs */)
  2099  	require.NoError(t, err)
  2100  
  2101  	tt.users[0].addTeamMember(parentName.String(), tt.users[1].username, keybase1.TeamRole_ADMIN)
  2102  	tt.users[0].addTeamMember(subteamName.String(), tt.users[1].username, keybase1.TeamRole_ADMIN)
  2103  
  2104  	parentid, err := teams.ResolveNameToID(context.TODO(), tt.users[0].tc.G, parentName)
  2105  	require.NoError(t, err)
  2106  	err = teams.RotateKey(context.TODO(), tt.users[0].tc.G, keybase1.TeamRotateKeyArg{TeamID: parentid, Rt: keybase1.RotationType_HIDDEN})
  2107  	require.NoError(t, err)
  2108  
  2109  	_, err = teams.Load(context.Background(), tt.users[1].tc.G, keybase1.LoadTeamArg{Name: subteamName.String()})
  2110  	require.NoError(t, err)
  2111  
  2112  	_, err = teams.Load(context.Background(), tt.users[1].tc.G,
  2113  		keybase1.LoadTeamArg{Name: parentName.String()})
  2114  	require.NoError(t, err)
  2115  }
  2116  
  2117  func TestTeamHiddenGenerationRotateRace(t *testing.T) {
  2118  	tt := newTeamTester(t)
  2119  	defer tt.cleanup()
  2120  
  2121  	tt.addUser("alf")
  2122  	tt.addUser("bra")
  2123  	tt.addUser("cha")
  2124  
  2125  	alice := tt.users[0]
  2126  	bob := tt.users[1]
  2127  	charlie := tt.users[2]
  2128  
  2129  	team := alice.createTeam()
  2130  	parentName, err := keybase1.TeamNameFromString(team)
  2131  	require.NoError(t, err)
  2132  	_, err = teams.CreateSubteam(context.TODO(), alice.tc.G, "bb",
  2133  		parentName, keybase1.TeamRole_NONE /* addSelfAs */)
  2134  	require.NoError(t, err)
  2135  	subteamName, err := parentName.Append("bb")
  2136  	require.NoError(t, err)
  2137  	_, err = teams.CreateSubteam(context.TODO(), alice.tc.G, "cc",
  2138  		subteamName, keybase1.TeamRole_NONE /* addSelfAs */)
  2139  	require.NoError(t, err)
  2140  	subsubteamName, err := subteamName.Append("cc")
  2141  	require.NoError(t, err)
  2142  
  2143  	t.Logf("Start testing")
  2144  
  2145  	alice.addTeamMember(subsubteamName.String(), charlie.username, keybase1.TeamRole_ADMIN)
  2146  	charlie.waitForMetadataUpdateGregor("added to team")
  2147  
  2148  	alice.addTeamMember(parentName.String(), bob.username, keybase1.TeamRole_ADMIN)
  2149  	bob.waitForMetadataUpdateGregor("added to team")
  2150  
  2151  	alice.removeTeamMember(parentName.String(), bob.username)
  2152  	bob.waitForMetadataUpdateGregor("removed from team")
  2153  
  2154  	alice.addTeamMember(parentName.String(), bob.username, keybase1.TeamRole_ADMIN)
  2155  	bob.waitForMetadataUpdateGregor("added back")
  2156  }