1package systests
2
3import (
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
15func divDebug(ctx *smuContext, fmt string, arg ...interface{}) {
16	div := "------------"
17	ctx.log.Debug(div+" "+fmt+" "+div, arg...)
18}
19
20func 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.
57func 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
116func 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
205func 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.
252func 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
280func 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
319func 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
349func 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
384func 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.
414func 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
464func 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
518func 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
546func 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
591func TestTeamReAddAfterReset(t *testing.T) {
592	testTeamReAddAfterReset(t, true, false, false)
593}
594
595func TestTeamReAddAfterResetPukless(t *testing.T) {
596	testTeamReAddAfterReset(t, false, false, false)
597}
598
599func TestTeamReAddAfterResetAdminOwner(t *testing.T) {
600	testTeamReAddAfterReset(t, true, true, false)
601}
602
603func TestTeamReAddAfterResetAdminOwnerPukless(t *testing.T) {
604	testTeamReAddAfterReset(t, false, true, false)
605}
606
607func TestTeamResetReAddRemoveAdminOwner(t *testing.T) {
608	testTeamReAddAfterReset(t, true, true, true)
609}
610
611func 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.
618func 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
731func 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
777func 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
820func 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
855func 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.
899func 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.
906func TestTeamResetBadgesOnRemove(t *testing.T) {
907	testTeamResetBadgesAndDismiss(t, false)
908}
909
910// Test users leaving the team when their eldest seqno is not 1.
911func 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}
934