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

     1  package systests
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"golang.org/x/net/context"
     8  
     9  	libkb "github.com/keybase/client/go/libkb"
    10  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    11  	teams "github.com/keybase/client/go/teams"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func divDebug(ctx *smuContext, fmt string, arg ...interface{}) {
    16  	div := "------------"
    17  	ctx.log.Debug(div+" "+fmt+" "+div, arg...)
    18  }
    19  
    20  func pollForMembershipUpdate(team smuTeam, ann *smuUser, bob *smuUser, cam *smuUser) {
    21  
    22  	// Keep reloading this team until we get that Bob has been deactivated.
    23  	// It might happen after the team is rotated, since a cache bust via gregor has
    24  	// to happen
    25  	poller := func(d keybase1.TeamDetails) bool {
    26  		for _, member := range d.Members.Writers {
    27  			if member.Username == bob.username {
    28  				return member.Status.IsReset()
    29  			}
    30  		}
    31  		return false
    32  	}
    33  
    34  	details := ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), poller)
    35  	for _, member := range details.Members.Admins {
    36  		switch member.Username {
    37  		case ann.username:
    38  			require.True(ann.ctx.t, member.Status.IsActive())
    39  		default:
    40  			require.Fail(ann.ctx.t, "unknown admin: %s", member.Username)
    41  		}
    42  	}
    43  	for _, member := range details.Members.Writers {
    44  		switch member.Username {
    45  		case bob.username:
    46  			require.True(ann.ctx.t, member.Status.IsReset())
    47  		case cam.username:
    48  			require.True(ann.ctx.t, member.Status.IsActive())
    49  		default:
    50  			require.Fail(ann.ctx.t, "unknown writer: %s (%+v)", member.Username, details)
    51  		}
    52  	}
    53  	ann.ctx.log.Debug("team details checked out: %+v", details)
    54  }
    55  
    56  // tests a user deleting her account.
    57  func TestTeamDelete(t *testing.T) {
    58  	ctx := newSMUContext(t)
    59  	defer ctx.cleanup()
    60  
    61  	ann := ctx.installKeybaseForUser("ann", 10)
    62  	ann.signup()
    63  	divDebug(ctx, "Signed up ann (%s)", ann.username)
    64  	bob := ctx.installKeybaseForUser("bob", 10)
    65  	bob.signup()
    66  	divDebug(ctx, "Signed up bob (%s)", bob.username)
    67  	cam := ctx.installKeybaseForUser("cam", 10)
    68  	cam.signup()
    69  	divDebug(ctx, "Signed up cam (%s)", cam.username)
    70  
    71  	team := ann.createTeam([]*smuUser{bob, cam})
    72  	divDebug(ctx, "team created (%s)", team.name)
    73  
    74  	ann.sendChat(team, "0")
    75  	divDebug(ctx, "Sent chat '0' (%s via %s)", team.name, ann.username)
    76  
    77  	ann.readChats(team, 1)
    78  	bob.readChats(team, 1)
    79  	divDebug(ctx, "Ann and bob can read")
    80  
    81  	// just one person needs to do this before ann deletes, so her
    82  	// deletion will immediately fall into accelerated rekeyd.
    83  	kickTeamRekeyd(bob.getPrimaryGlobalContext(), t)
    84  
    85  	ann.delete()
    86  	divDebug(ctx, "Ann deleted her account")
    87  	divDebug(ctx, "ann uid: %s", ann.uid())
    88  	divDebug(ctx, "bob uid: %s", bob.uid())
    89  	divDebug(ctx, "cam uid: %s", cam.uid())
    90  
    91  	// bob and cam should see the key get rotated after ann deletes
    92  	bob.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), nil)
    93  	cam.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), nil)
    94  
    95  	// It's important for cam to clear her cache right before the attempt to send,
    96  	// since she might have received gregors that ann deleted her account,
    97  	// and therefore might be trying to refresh and load the team.
    98  	cam.primaryDevice().clearUPAKCache()
    99  	cam.sendChat(team, "1")
   100  
   101  	divDebug(ctx, "Cam sent a chat")
   102  	bob.readChats(team, 2)
   103  
   104  	// Disable UIDMapper cache to be able to see current state of
   105  	// Active/Inactive for members.
   106  	bob.setUIDMapperNoCachingMode(true)
   107  	cam.setUIDMapperNoCachingMode(true)
   108  
   109  	bob.assertMemberMissing(team, ann)
   110  	bob.assertMemberActive(team, cam)
   111  
   112  	cam.assertMemberMissing(team, ann)
   113  	cam.assertMemberActive(team, bob)
   114  }
   115  
   116  func TestTeamReset(t *testing.T) {
   117  	ctx := newSMUContext(t)
   118  	defer ctx.cleanup()
   119  
   120  	ann := ctx.installKeybaseForUser("ann", 10)
   121  	ann.signup()
   122  	divDebug(ctx, "Signed up ann (%s, %s)", ann.username, ann.uid())
   123  	bob := ctx.installKeybaseForUser("bob", 10)
   124  	bob.signup()
   125  	divDebug(ctx, "Signed up bob (%s, %s)", bob.username, bob.uid())
   126  	cam := ctx.installKeybaseForUser("cam", 10)
   127  	cam.signup()
   128  	divDebug(ctx, "Signed up cam (%s, %s)", cam.username, cam.uid())
   129  
   130  	// Note that ann (the admin) has a UIDMapper that should get pubsub updates
   131  	// since she is an admin for the team in question. cam won't get those
   132  	// pubsub updates
   133  	ann.setUIDMapperNoCachingMode(true)
   134  	bob.setUIDMapperNoCachingMode(true)
   135  	cam.setUIDMapperNoCachingMode(true)
   136  
   137  	team := ann.createTeam([]*smuUser{bob, cam})
   138  	divDebug(ctx, "team created (%s)", team.name)
   139  
   140  	// ensure bob is active according to other users
   141  	ann.assertMemberActive(team, bob)
   142  	cam.assertMemberActive(team, bob)
   143  
   144  	ann.sendChat(team, "0")
   145  	divDebug(ctx, "Sent chat '0' (%s via %s)", team.name, ann.username)
   146  
   147  	ann.readChats(team, 1)
   148  	bob.readChats(team, 1)
   149  
   150  	kickTeamRekeyd(bob.getPrimaryGlobalContext(), t)
   151  	bob.reset()
   152  	divDebug(ctx, "Reset bob (%s)", bob.username)
   153  
   154  	pollForMembershipUpdate(team, ann, bob, cam)
   155  	divDebug(ctx, "Polled for rekey")
   156  
   157  	// bob should be inactive according to other users
   158  	ann.assertMemberInactive(team, bob)
   159  	cam.assertMemberInactive(team, bob)
   160  
   161  	bob.loginAfterReset(10)
   162  	divDebug(ctx, "Bob logged in after reset")
   163  
   164  	// bob should be inactive according to other users
   165  	ann.assertMemberInactive(team, bob)
   166  	cam.assertMemberInactive(team, bob)
   167  
   168  	_, err := bob.teamGet(team)
   169  	require.Error(t, err)
   170  	ae, ok := err.(libkb.AppStatusError)
   171  	require.True(t, ok)
   172  	require.Equal(t, ae.Code, int(keybase1.StatusCode_SCTeamReadError))
   173  	divDebug(ctx, "Bob failed to read the team")
   174  
   175  	// Make sure that ann can still send even though bob is ousted
   176  	ann.sendChat(team, "1")
   177  	divDebug(ctx, "Sent chat '1' (%s via %s)", team.name, ann.username)
   178  	ann.readChats(team, 2)
   179  	// Same goes for cam --- note that she never read before, so nothing
   180  	// is cached for her.
   181  	cam.readChats(team, 2)
   182  
   183  	_, err = bob.readChatsWithError(team)
   184  	require.Error(t, err)
   185  	ae, ok = err.(libkb.AppStatusError)
   186  	require.True(t, ok)
   187  	require.Equal(t, ae.Code, int(keybase1.StatusCode_SCTeamReadError))
   188  	divDebug(ctx, "Bob failed to read the chat")
   189  
   190  	ann.addWriter(team, bob)
   191  	divDebug(ctx, "Added bob back as a writer")
   192  	_, err = bob.teamGet(team)
   193  	require.NoError(t, err)
   194  	divDebug(ctx, "Bob could read the team after added back")
   195  	bob.readChats(team, 2)
   196  	divDebug(ctx, "Bob reading chats after added back")
   197  	ann.sendChat(team, "2")
   198  	divDebug(ctx, "Ann sending chat '2'")
   199  	bob.readChats(team, 3)
   200  	divDebug(ctx, "Bob reading chat '2'")
   201  }
   202  
   203  // add bob (a user who has reset his account) to a team
   204  // that he was never a member of
   205  func TestTeamResetAdd(t *testing.T) {
   206  	ctx := newSMUContext(t)
   207  	defer ctx.cleanup()
   208  
   209  	ann := ctx.installKeybaseForUser("ann", 10)
   210  	ann.signup()
   211  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   212  	bob := ctx.installKeybaseForUser("bob", 10)
   213  	bob.signup()
   214  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   215  	cam := ctx.installKeybaseForUser("cam", 10)
   216  	cam.signup()
   217  	divDebug(ctx, "Signed up cam (%s)", cam.username)
   218  
   219  	team := ann.createTeam([]*smuUser{cam})
   220  	divDebug(ctx, "team created (%s)", team.name)
   221  
   222  	ann.sendChat(team, "0")
   223  	divDebug(ctx, "Sent chat '2' (%s via %s)", team.name, ann.username)
   224  
   225  	ann.readChats(team, 1)
   226  
   227  	bob.reset()
   228  	divDebug(ctx, "Reset bob (%s)", bob.username)
   229  
   230  	bob.loginAfterReset(10)
   231  	divDebug(ctx, "Bob logged in after reset")
   232  
   233  	_, err := bob.teamGet(team)
   234  	require.Error(t, err)
   235  	divDebug(ctx, "Bob failed to read the team")
   236  
   237  	ann.addWriter(team, bob)
   238  	divDebug(ctx, "Added bob as a writer")
   239  	_, err = bob.teamGet(team)
   240  	require.NoError(t, err)
   241  	divDebug(ctx, "Bob could read the team after added")
   242  	bob.readChats(team, 1)
   243  	divDebug(ctx, "Bob reading chats after added")
   244  	ann.sendChat(team, "1")
   245  	divDebug(ctx, "Ann sending chat '2'")
   246  	bob.readChats(team, 2)
   247  	divDebug(ctx, "Bob reading chat '2'")
   248  }
   249  
   250  // Ann creates a team, and adds Bob as an admin. Then Alice resets, and Bob readmits
   251  // Ann as an admin (since he can't make her an owner). It should work.
   252  func TestTeamOwnerResetAdminReadmit(t *testing.T) {
   253  	ctx := newSMUContext(t)
   254  	defer ctx.cleanup()
   255  
   256  	ann := ctx.installKeybaseForUser("ann", 10)
   257  	ann.signup()
   258  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   259  	bob := ctx.installKeybaseForUser("bob", 10)
   260  	bob.signup()
   261  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   262  
   263  	team := ann.createTeam([]*smuUser{})
   264  	divDebug(ctx, "team created (%s)", team.name)
   265  	ann.addAdmin(team, bob)
   266  
   267  	ann.reset()
   268  	divDebug(ctx, "Reset ann (%s)", ann.username)
   269  
   270  	ann.loginAfterReset(2)
   271  	divDebug(ctx, "Ann logged in after reset")
   272  	bob.addAdmin(team, ann)
   273  	_, err := ann.teamGet(team)
   274  	require.NoError(t, err)
   275  	divDebug(ctx, "Ann read the team")
   276  }
   277  
   278  // add bob (a user who has reset his account and has no PUK) to a team
   279  // that he was never a member of
   280  func TestTeamResetAddNoPUK(t *testing.T) {
   281  	ctx := newSMUContext(t)
   282  	defer ctx.cleanup()
   283  
   284  	ann := ctx.installKeybaseForUser("ann", 10)
   285  	ann.signup()
   286  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   287  	bob := ctx.installKeybaseForUserNoPUK("bob", 10)
   288  	bob.signupNoPUK()
   289  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   290  	cam := ctx.installKeybaseForUser("cam", 10)
   291  	cam.signup()
   292  	divDebug(ctx, "Signed up cam (%s)", cam.username)
   293  
   294  	team := ann.createTeam([]*smuUser{cam})
   295  	divDebug(ctx, "team created (%s)", team.name)
   296  
   297  	ann.sendChat(team, "0")
   298  	divDebug(ctx, "Sent chat '2' (%s via %s)", team.name, ann.username)
   299  
   300  	ann.readChats(team, 1)
   301  
   302  	bob.reset()
   303  	divDebug(ctx, "Reset bob (%s)", bob.username)
   304  
   305  	bob.loginAfterResetNoPUK(1)
   306  	divDebug(ctx, "Bob logged in after reset")
   307  
   308  	_, err := bob.teamGet(team)
   309  	require.Error(t, err)
   310  	divDebug(ctx, "Bob failed to read the team")
   311  
   312  	// this is the main point of the test, to get this to work
   313  	// without an eldest seqno error.
   314  	ann.addWriter(team, bob)
   315  	divDebug(ctx, "Added bob as a writer")
   316  }
   317  
   318  // bob resets and added with no keys
   319  func TestTeamResetNoKeys(t *testing.T) {
   320  	ctx := newSMUContext(t)
   321  	defer ctx.cleanup()
   322  
   323  	ann := ctx.installKeybaseForUser("ann", 10)
   324  	ann.signup()
   325  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   326  	bob := ctx.installKeybaseForUser("bob", 10)
   327  	bob.signup()
   328  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   329  	cam := ctx.installKeybaseForUser("cam", 10)
   330  	cam.signup()
   331  	divDebug(ctx, "Signed up cam (%s)", cam.username)
   332  
   333  	team := ann.createTeam([]*smuUser{cam})
   334  	divDebug(ctx, "team created (%s)", team.name)
   335  
   336  	ann.sendChat(team, "0")
   337  	divDebug(ctx, "Sent chat '2' (%s via %s)", team.name, ann.username)
   338  
   339  	ann.readChats(team, 1)
   340  
   341  	bob.reset()
   342  	divDebug(ctx, "Reset bob (%s)", bob.username)
   343  
   344  	ann.addWriter(team, bob)
   345  	divDebug(ctx, "Added bob as a writer")
   346  }
   347  
   348  // bob resets several times and added with no keys
   349  func TestTeamResetManyNoKeys(t *testing.T) {
   350  	ctx := newSMUContext(t)
   351  	defer ctx.cleanup()
   352  
   353  	ann := ctx.installKeybaseForUser("ann", 10)
   354  	ann.signup()
   355  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   356  	bob := ctx.installKeybaseForUser("bob", 10)
   357  	bob.signup()
   358  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   359  	cam := ctx.installKeybaseForUser("cam", 10)
   360  	cam.signup()
   361  	divDebug(ctx, "Signed up cam (%s)", cam.username)
   362  
   363  	team := ann.createTeam([]*smuUser{cam})
   364  	divDebug(ctx, "team created (%s)", team.name)
   365  
   366  	ann.sendChat(team, "0")
   367  	divDebug(ctx, "Sent chat '2' (%s via %s)", team.name, ann.username)
   368  
   369  	ann.readChats(team, 1)
   370  
   371  	for i := 0; i < 5; i++ {
   372  		bob.reset()
   373  		divDebug(ctx, "Reset bob (%s)", bob.username)
   374  
   375  		bob.loginAfterReset(10)
   376  		divDebug(ctx, "Bob logged in after reset")
   377  	}
   378  
   379  	ann.addWriter(team, bob)
   380  	divDebug(ctx, "Added bob as a writer")
   381  }
   382  
   383  // bob resets and has no keys, added as an admin
   384  func TestTeamResetNoKeysAdmin(t *testing.T) {
   385  	ctx := newSMUContext(t)
   386  	defer ctx.cleanup()
   387  
   388  	ann := ctx.installKeybaseForUser("ann", 10)
   389  	ann.signup()
   390  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   391  	bob := ctx.installKeybaseForUser("bob", 10)
   392  	bob.signup()
   393  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   394  	cam := ctx.installKeybaseForUser("cam", 10)
   395  	cam.signup()
   396  	divDebug(ctx, "Signed up cam (%s)", cam.username)
   397  
   398  	team := ann.createTeam([]*smuUser{cam})
   399  	divDebug(ctx, "team created (%s)", team.name)
   400  
   401  	ann.sendChat(team, "0")
   402  	divDebug(ctx, "Sent chat '2' (%s via %s)", team.name, ann.username)
   403  
   404  	ann.readChats(team, 1)
   405  
   406  	bob.reset()
   407  	divDebug(ctx, "Reset bob (%s)", bob.username)
   408  
   409  	ann.addAdmin(team, bob)
   410  	divDebug(ctx, "Added bob as an admin")
   411  }
   412  
   413  // Remove a member who was in a team and reset.
   414  func TestTeamRemoveAfterReset(t *testing.T) {
   415  	ctx := newSMUContext(t)
   416  	defer ctx.cleanup()
   417  
   418  	ann := ctx.installKeybaseForUser("ann", 10)
   419  	ann.signup()
   420  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   421  	bob := ctx.installKeybaseForUser("bob", 10)
   422  	bob.signup()
   423  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   424  	joe := ctx.installKeybaseForUser("joe", 10)
   425  	joe.signup()
   426  	divDebug(ctx, "Signed up joe (%s)", joe.username)
   427  
   428  	team := ann.createTeam([]*smuUser{bob, joe})
   429  	divDebug(ctx, "team created (%s)", team.name)
   430  
   431  	kickTeamRekeyd(ann.getPrimaryGlobalContext(), t)
   432  	bob.reset()
   433  	divDebug(ctx, "Reset bob (%s)", bob.username)
   434  
   435  	bob.loginAfterReset(10)
   436  	divDebug(ctx, "Bob logged in after reset")
   437  	ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), nil)
   438  
   439  	joe.reset()
   440  	divDebug(ctx, "Reset joe (%s), not re-provisioning though!", joe.username)
   441  
   442  	ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(3), nil)
   443  
   444  	cli := ann.getTeamsClient()
   445  	err := cli.TeamRemoveMember(context.TODO(), keybase1.TeamRemoveMemberArg{
   446  		TeamID: team.ID,
   447  		Member: keybase1.NewTeamMemberToRemoveWithAssertion(keybase1.AssertionTeamMemberToRemove{Assertion: bob.username}),
   448  	})
   449  	require.NoError(t, err)
   450  
   451  	err = cli.TeamRemoveMember(context.TODO(), keybase1.TeamRemoveMemberArg{
   452  		TeamID: team.ID,
   453  		Member: keybase1.NewTeamMemberToRemoveWithAssertion(keybase1.AssertionTeamMemberToRemove{Assertion: joe.username}),
   454  	})
   455  	require.NoError(t, err)
   456  
   457  	G := ann.getPrimaryGlobalContext()
   458  	teams.NewTeamLoaderAndInstall(G)
   459  	role, err := teams.MemberRole(context.TODO(), G, team.name, bob.username)
   460  	require.NoError(t, err)
   461  	require.Equal(t, role, keybase1.TeamRole_NONE)
   462  }
   463  
   464  func TestTeamRemoveMemberAfterDelete(t *testing.T) {
   465  	ctx := newSMUContext(t)
   466  	defer ctx.cleanup()
   467  
   468  	ann := ctx.installKeybaseForUser("ann", 10)
   469  	ann.signup()
   470  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   471  	bob := ctx.installKeybaseForUser("bob", 10)
   472  	bob.signup()
   473  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   474  
   475  	team := ann.createTeam([]*smuUser{bob})
   476  	divDebug(ctx, "team created (%s)", team.name)
   477  
   478  	bobUID := bob.uid()
   479  
   480  	bob.delete()
   481  	divDebug(ctx, "Bob deleted (%s)", bob.username)
   482  
   483  	ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), nil)
   484  
   485  	// Ensure ann sees bob as deleted, and not some cached remnant of
   486  	// the past.
   487  	ann.primaryDevice().clearUPAKCache()
   488  	G := ann.getPrimaryGlobalContext()
   489  	arg := libkb.NewLoadUserArg(G).WithNetContext(context.Background()).
   490  		WithUID(bobUID).WithPublicKeyOptional()
   491  	upak, _, err := G.GetUPAKLoader().LoadV2(arg)
   492  	require.NoError(t, err)
   493  	require.EqualValues(t, libkb.SCDeleted, upak.Current.Status)
   494  
   495  	cli := ann.getTeamsClient()
   496  	err = cli.TeamRemoveMember(context.Background(), keybase1.TeamRemoveMemberArg{
   497  		TeamID: team.ID,
   498  		Member: keybase1.NewTeamMemberToRemoveWithAssertion(keybase1.AssertionTeamMemberToRemove{Assertion: bob.username}),
   499  	})
   500  	require.NoError(t, err)
   501  
   502  	t.Logf("Calling TeamGet")
   503  
   504  	details, err := cli.TeamGet(context.Background(), keybase1.TeamGetArg{
   505  		Name: team.name,
   506  	})
   507  	require.NoError(t, err)
   508  
   509  	require.Equal(t, 1, len(details.Members.Owners))
   510  	require.Equal(t, ann.username, details.Members.Owners[0].Username)
   511  	require.Equal(t, 0, len(details.Members.Admins))
   512  	require.Equal(t, 0, len(details.Members.Writers))
   513  	require.Equal(t, 0, len(details.Members.Readers))
   514  	require.Equal(t, 0, len(details.Members.Bots))
   515  	require.Equal(t, 0, len(details.Members.RestrictedBots))
   516  }
   517  
   518  func TestTeamTryAddDeletedUser(t *testing.T) {
   519  	ctx := newSMUContext(t)
   520  	defer ctx.cleanup()
   521  
   522  	ann := ctx.installKeybaseForUser("ann", 10)
   523  	ann.signup()
   524  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   525  
   526  	bob := ctx.installKeybaseForUser("bob", 10)
   527  	bob.signup()
   528  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   529  	bob.delete()
   530  	divDebug(ctx, "Bob deleted (%s)", bob.username)
   531  
   532  	cli := ann.getTeamsClient()
   533  	team := ann.createTeam([]*smuUser{})
   534  	divDebug(ctx, "team created (%s)", team.name)
   535  
   536  	_, err := cli.TeamAddMember(context.Background(), keybase1.TeamAddMemberArg{
   537  		TeamID:   team.ID,
   538  		Username: bob.username,
   539  		Role:     keybase1.TeamRole_READER,
   540  	})
   541  	require.Error(t, err)
   542  }
   543  
   544  // Add a member after reset in a normal (non-implicit) team
   545  // Uses Add not Readd
   546  func TestTeamAddAfterReset(t *testing.T) {
   547  	ctx := newSMUContext(t)
   548  	defer ctx.cleanup()
   549  
   550  	ann := ctx.installKeybaseForUser("ann", 10)
   551  	ann.signup()
   552  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   553  	bob := ctx.installKeybaseForUser("bob", 10)
   554  	bob.signup()
   555  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   556  
   557  	team := ann.createTeam([]*smuUser{bob})
   558  	divDebug(ctx, "team created (%s)", team.name)
   559  
   560  	ann.sendChat(team, "0")
   561  
   562  	kickTeamRekeyd(ann.getPrimaryGlobalContext(), t)
   563  	bob.reset()
   564  	divDebug(ctx, "Reset bob (%s)", bob.username)
   565  
   566  	bob.loginAfterReset(10)
   567  	divDebug(ctx, "Bob logged in after reset")
   568  
   569  	ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), nil)
   570  
   571  	cli := ann.getTeamsClient()
   572  	_, err := cli.TeamAddMember(context.TODO(), keybase1.TeamAddMemberArg{
   573  		TeamID:   team.ID,
   574  		Username: bob.username,
   575  		// Note: any role would do! Does not have to be the same as before
   576  		// reset. This does not apply to imp-teams though, it requires the
   577  		// same role there.
   578  		Role: keybase1.TeamRole_READER,
   579  	})
   580  	require.NoError(t, err)
   581  
   582  	G := ann.getPrimaryGlobalContext()
   583  	teams.NewTeamLoaderAndInstall(G)
   584  	role, err := teams.MemberRole(context.TODO(), G, team.name, bob.username)
   585  	require.NoError(t, err)
   586  	require.Equal(t, role, keybase1.TeamRole_READER)
   587  
   588  	bob.readChats(team, 1)
   589  }
   590  
   591  func TestTeamReAddAfterReset(t *testing.T) {
   592  	testTeamReAddAfterReset(t, true, false, false)
   593  }
   594  
   595  func TestTeamReAddAfterResetPukless(t *testing.T) {
   596  	testTeamReAddAfterReset(t, false, false, false)
   597  }
   598  
   599  func TestTeamReAddAfterResetAdminOwner(t *testing.T) {
   600  	testTeamReAddAfterReset(t, true, true, false)
   601  }
   602  
   603  func TestTeamReAddAfterResetAdminOwnerPukless(t *testing.T) {
   604  	testTeamReAddAfterReset(t, false, true, false)
   605  }
   606  
   607  func TestTeamResetReAddRemoveAdminOwner(t *testing.T) {
   608  	testTeamReAddAfterReset(t, true, true, true)
   609  }
   610  
   611  func TestTeamResetReAddRemoveAdminOwnerPukless(t *testing.T) {
   612  	testTeamReAddAfterReset(t, false, true, true)
   613  }
   614  
   615  // Add a member after reset in a normal (non-implicit) team
   616  // pukful - re-add the user after they get a puk
   617  // adminOwner - an admin is re-adding an owner.
   618  func testTeamReAddAfterReset(t *testing.T, pukful, adminOwner, removeAfterReset bool) {
   619  	if removeAfterReset && !adminOwner {
   620  		require.FailNow(t, "nope")
   621  	}
   622  	ctx := newSMUContext(t)
   623  	defer ctx.cleanup()
   624  
   625  	ann := ctx.installKeybaseForUser("ann", 10)
   626  	ann.signup()
   627  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   628  	bob := ctx.installKeybaseForUser("bob", 10)
   629  	bob.signup()
   630  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   631  
   632  	var team smuTeam
   633  	if adminOwner {
   634  		// Create a team where ann is an admin and bob is an owner.
   635  		team = ann.createTeam2(nil, nil, nil, []*smuUser{bob})
   636  		ann.editMember(&team, ann.username, keybase1.TeamRole_ADMIN)
   637  	} else {
   638  		team = ann.createTeam([]*smuUser{bob})
   639  	}
   640  	divDebug(ctx, "team created (%s) (%v)", team.name, team.ID)
   641  
   642  	bobUVBeforeReset := bob.userVersion()
   643  
   644  	ann.sendChat(team, "0")
   645  	kickTeamRekeyd(ann.getPrimaryGlobalContext(), t)
   646  	bob.reset()
   647  	divDebug(ctx, "Reset bob (%s)", bob.username)
   648  
   649  	if pukful {
   650  		// Bob gets a puk BEFORE being re-added.
   651  		bob.loginAfterReset(10)
   652  		divDebug(ctx, "Bob logged in after reset")
   653  	}
   654  
   655  	ann.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), nil)
   656  
   657  	cli := ann.getTeamsClient()
   658  	err := cli.TeamReAddMemberAfterReset(context.TODO(), keybase1.TeamReAddMemberAfterResetArg{
   659  		Id:       team.ID,
   660  		Username: bob.username,
   661  	})
   662  	require.NoError(t, err)
   663  
   664  	if removeAfterReset {
   665  		err := ann.getTeamsClient().TeamRemoveMember(context.TODO(), keybase1.TeamRemoveMemberArg{
   666  			TeamID: team.ID,
   667  			Member: keybase1.NewTeamMemberToRemoveWithAssertion(keybase1.AssertionTeamMemberToRemove{Assertion: bob.username}),
   668  		})
   669  		require.NoError(t, err)
   670  		return
   671  	}
   672  
   673  	if !pukful {
   674  		// Bob gets a puk AFTER being re-added.
   675  		bob.loginAfterReset(10)
   676  		divDebug(ctx, "Bob logged in after reset")
   677  	}
   678  
   679  	expectedRole := keybase1.TeamRole_WRITER
   680  	if adminOwner {
   681  		// The reset owner should be re-admitted as an owner since
   682  		// that's the highest power ann can grant.
   683  		expectedRole = keybase1.TeamRole_ADMIN
   684  	}
   685  
   686  	teams.NewTeamLoaderAndInstall(ann.getPrimaryGlobalContext())
   687  
   688  	pollFn := func(_ int) bool {
   689  		G := ann.getPrimaryGlobalContext()
   690  		teamObj, err := teams.Load(context.TODO(), G, keybase1.LoadTeamArg{
   691  			ID:          team.ID,
   692  			NeedAdmin:   true,
   693  			ForceRepoll: true,
   694  		})
   695  		require.NoError(t, err)
   696  
   697  		role, err := teamObj.MemberRole(context.TODO(), bob.userVersion())
   698  		require.NoError(t, err)
   699  		if role == keybase1.TeamRole_NONE {
   700  			return false
   701  		}
   702  		if role == expectedRole {
   703  			return true
   704  		}
   705  		require.FailNowf(t, "unexpected role", "got %v on the hunt for %v", role, expectedRole)
   706  
   707  		// Old UV should be gone. Note that the server would not have allowed
   708  		// adding new UV without removing the old one first, so if the old UV
   709  		// is not being removed correctly during SBS handling, this test would
   710  		// probably never get to the following assertions (unless that server
   711  		// logic has changed.)
   712  		role, err = teamObj.MemberRole(context.TODO(), bobUVBeforeReset)
   713  		require.NoError(t, err)
   714  		require.Equal(t, keybase1.TeamRole_NONE, role)
   715  		return false
   716  	}
   717  
   718  	if pukful {
   719  		// Bob should have been synchronously made a cyrptomember by re-add.
   720  		require.Equal(t, true, pollFn(0))
   721  	} else {
   722  		// A background task should upgrade bob from an invite to a cryptomember.
   723  		pollTime := 10 * time.Second
   724  		pollFor(t, "bob to be upgraded from invite to cryptomember",
   725  			pollTime, ann.getPrimaryGlobalContext(), pollFn)
   726  	}
   727  
   728  	bob.readChats(team, 1)
   729  }
   730  
   731  func TestResetInOpenTeam(t *testing.T) {
   732  	ctx := newSMUContext(t)
   733  	defer ctx.cleanup()
   734  
   735  	ann := ctx.installKeybaseForUser("ann", 10)
   736  	ann.signup()
   737  	divDebug(ctx, "Signed up ann (%s)", ann.username)
   738  	bob := ctx.installKeybaseForUser("bob", 10)
   739  	bob.signup()
   740  	divDebug(ctx, "Signed up bob (%s)", bob.username)
   741  
   742  	team := ann.createTeam([]*smuUser{bob})
   743  	divDebug(ctx, "team created (%s)", team.name)
   744  	ann.openTeam(team, keybase1.TeamRole_WRITER)
   745  	ann.assertMemberActive(team, bob)
   746  
   747  	enableOpenSweepForTeam(ann.getPrimaryGlobalContext(), t, team.ID)
   748  
   749  	kickTeamRekeyd(ann.getPrimaryGlobalContext(), t)
   750  	bob.reset()
   751  	divDebug(ctx, "Reset bob (%s)", bob.username)
   752  
   753  	// Wait for OPENSWEEP which will remove bob from the team posting link 4.
   754  	ann.pollForTeamSeqnoLink(team, keybase1.Seqno(4))
   755  
   756  	teamObj := ann.loadTeam(team.name, false)
   757  	_, err := teamObj.UserVersionByUID(context.Background(), bob.uid())
   758  	require.Error(t, err, "expecting reset user to be removed from the team")
   759  	require.Contains(t, err.Error(), "did not find user")
   760  	require.EqualValues(t, 4, teamObj.CurrentSeqno())
   761  	// Generation shouldn't change during OPENSWEEPing.
   762  	require.Equal(t, keybase1.PerTeamKeyGeneration(1), teamObj.Generation())
   763  
   764  	bob.loginAfterReset(10)
   765  	divDebug(ctx, "Bob logged in after reset")
   766  
   767  	bob.requestAccess(team)
   768  	divDebug(ctx, "Bob requested access to open team after reset")
   769  
   770  	ann.pollForTeamSeqnoLink(team, teamObj.NextSeqno())
   771  	ann.assertMemberActive(team, bob)
   772  
   773  	teamObj = ann.loadTeam(team.name, false)
   774  	require.Equal(t, keybase1.PerTeamKeyGeneration(1), teamObj.Generation())
   775  }
   776  
   777  func TestTeamListAfterReset(t *testing.T) {
   778  	ctx := newSMUContext(t)
   779  	defer ctx.cleanup()
   780  
   781  	ann := ctx.installKeybaseForUser("ann", 10)
   782  	ann.signup()
   783  	divDebug(ctx, "Signed up ann (%s, %s)", ann.username, ann.uid())
   784  	bob := ctx.installKeybaseForUser("bob", 10)
   785  	bob.signup()
   786  	divDebug(ctx, "Signed up bob (%s, %s)", bob.username, bob.uid())
   787  	cam := ctx.installKeybaseForUser("cam", 10)
   788  	cam.signup()
   789  	divDebug(ctx, "Signed up cam (%s, %s)", cam.username, cam.uid())
   790  
   791  	team := ann.createTeam([]*smuUser{bob, cam})
   792  	divDebug(ctx, "team created (%s)", team.name)
   793  
   794  	// ensure bob is active according to other users
   795  	ann.assertMemberActive(team, bob)
   796  
   797  	bob.reset()
   798  	divDebug(ctx, "Reset bob (%s)", bob.username)
   799  
   800  	bob.loginAfterReset(10)
   801  	divDebug(ctx, "Bob logged in after reset")
   802  
   803  	ann.addWriter(team, bob)
   804  	divDebug(ctx, "Added bob back as a writer")
   805  
   806  	list, err := cam.teamGet(team)
   807  	require.NoError(t, err)
   808  	found := false
   809  	for _, w := range list.Members.Writers {
   810  		if w.Username == bob.username {
   811  			require.False(t, found, "wasn't found twice")
   812  			require.True(t, w.Uv.EldestSeqno > 1, "reset eldest seqno")
   813  			require.True(t, w.Status.IsActive(), "is active")
   814  			found = true
   815  		}
   816  	}
   817  	require.True(t, found, "we found bob (before he found us)")
   818  }
   819  
   820  func TestTeamAfterDeleteUser(t *testing.T) {
   821  	ctx := newSMUContext(t)
   822  	defer ctx.cleanup()
   823  
   824  	ann := ctx.installKeybaseForUser("ann", 10)
   825  	ann.signup()
   826  	divDebug(ctx, "Signed up ann (%s, %s)", ann.username, ann.uid())
   827  	bob := ctx.installKeybaseForUser("bob", 10)
   828  	bob.signup()
   829  	divDebug(ctx, "Signed up bob (%s, %s)", bob.username, bob.uid())
   830  
   831  	team := ann.createTeam([]*smuUser{bob})
   832  	divDebug(ctx, "team created (%s)", team.name)
   833  
   834  	ann.sendChat(team, "0")
   835  	divDebug(ctx, "Sent chat '0' (%s via %s)", team.name, ann.username)
   836  
   837  	kickTeamRekeyd(ann.getPrimaryGlobalContext(), t)
   838  	ann.delete()
   839  
   840  	bob.pollForMembershipUpdate(team, keybase1.PerTeamKeyGeneration(2), nil)
   841  
   842  	divDebug(ctx, "Deleted ann (%s)", ann.username)
   843  
   844  	_, err := bob.teamGet(team)
   845  	require.NoError(t, err)
   846  
   847  	bob.dbNuke()
   848  
   849  	_, err = bob.teamGet(team)
   850  	require.NoError(t, err)
   851  
   852  	bob.readChats(team, 1)
   853  }
   854  
   855  func testTeamResetBadgesAndDismiss(t *testing.T, readd bool) {
   856  	tt := newTeamTester(t)
   857  	defer tt.cleanup()
   858  
   859  	tt.addUser("own")
   860  	tt.addUser("roo")
   861  
   862  	teamID, teamName := tt.users[0].createTeam2()
   863  	tt.users[0].kickTeamRekeyd()
   864  	tt.users[0].addTeamMember(teamName.String(), tt.users[1].username, keybase1.TeamRole_WRITER)
   865  	tt.users[1].reset()
   866  	tt.users[0].waitForTeamChangedGregor(teamID, keybase1.Seqno(2))
   867  	// wait for badge state to have 1 team w/ reset member
   868  	badgeState := tt.users[0].waitForBadgeStateWithReset(1)
   869  
   870  	// users[0] should be badged since users[1] reset
   871  	require.True(t, len(badgeState.TeamsWithResetUsers) > 0)
   872  	out := badgeState.TeamsWithResetUsers[0]
   873  	require.Equal(t, out.Teamname, teamName.String())
   874  	require.Equal(t, out.Username, tt.users[1].username)
   875  
   876  	// users[1] logs in after reset
   877  	tt.users[1].loginAfterReset()
   878  
   879  	// Either re-adding or removing user from the team should clear
   880  	// the reset badge.
   881  	if readd {
   882  		// users[0] adds users[1] back to the team
   883  		tt.users[0].addTeamMember(teamName.String(), tt.users[1].username, keybase1.TeamRole_WRITER)
   884  	} else {
   885  		// users[0] removes users[1] from the team
   886  		tt.users[0].removeTeamMember(teamName.String(), tt.users[1].username)
   887  	}
   888  
   889  	// wait for badge state to have no teams w/ reset member
   890  	badgeState = tt.users[0].waitForBadgeStateWithReset(0)
   891  
   892  	// badge state should be cleared
   893  	require.Zero(t, len(badgeState.TeamsWithResetUsers))
   894  }
   895  
   896  // TestTeamResetBadges checks that badges show up for admins
   897  // when a member of the team resets, and that they are dismissed
   898  // when the reset user is added.
   899  func TestTeamResetBadgesOnAdd(t *testing.T) {
   900  	testTeamResetBadgesAndDismiss(t, true)
   901  }
   902  
   903  // TestTeamResetBadgesOnRemove checks that badges show up for admins
   904  // when a member of the team resets, and that they are dismissed
   905  // when the reset user is removed.
   906  func TestTeamResetBadgesOnRemove(t *testing.T) {
   907  	testTeamResetBadgesAndDismiss(t, false)
   908  }
   909  
   910  // Test users leaving the team when their eldest seqno is not 1.
   911  func TestTeamResetAfterReset(t *testing.T) {
   912  	tt := newTeamTester(t)
   913  	defer tt.cleanup()
   914  
   915  	alice := tt.addUser("alice")
   916  	bob := tt.addUser("bob")
   917  
   918  	bob.reset()
   919  	bob.loginAfterReset()
   920  	_, teamName := alice.createTeam2()
   921  	tn := teamName.String()
   922  	alice.addTeamMember(tn, bob.username, keybase1.TeamRole_OWNER)
   923  	bob.leave(tn)
   924  	bob.reset()
   925  	bob.loginAfterReset()
   926  	alice.addTeamMember(tn, bob.username, keybase1.TeamRole_WRITER)
   927  	bob.leave(tn)
   928  	bob.reset()
   929  	bob.loginAfterReset()
   930  	alice.addTeamMember(tn, bob.username, keybase1.TeamRole_OWNER)
   931  	bob.leave(tn)
   932  	alice.loadTeam(tn, false)
   933  }