1package systests
2
3import (
4	"testing"
5	"time"
6
7	"github.com/keybase/client/go/libkb"
8	keybase1 "github.com/keybase/client/go/protocol/keybase1"
9	"github.com/keybase/client/go/teams"
10	"github.com/keybase/clockwork"
11	"github.com/stretchr/testify/require"
12	context "golang.org/x/net/context"
13)
14
15func TestTeamInviteSeitanInvitelinkHappy(t *testing.T) {
16	testTeamInviteSeitanInvitelinkHappy(t, false /* implicitAdmin */)
17	testTeamInviteSeitanInvitelinkHappy(t, true /* implicitAdmin */)
18}
19
20func testTeamInviteSeitanInvitelinkHappy(t *testing.T, implicitAdmin bool) {
21	tt := newTeamTester(t)
22	defer tt.cleanup()
23
24	alice := tt.addUser("kvr")
25	bob := tt.addUser("eci")
26
27	teamIDParent, teamNameParent := alice.createTeam2()
28	teamID := teamIDParent
29	teamName := teamNameParent
30	t.Logf("Created team %v %v", teamIDParent, teamNameParent)
31	if implicitAdmin {
32		subteamID, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "sub1", teamNameParent, keybase1.TeamRole_NONE /* addSelfAs */)
33		require.NoError(t, err)
34		teamID = *subteamID
35		subteamName, err := teamNameParent.Append("sub1")
36		require.NoError(t, err)
37		teamName = subteamName
38		t.Logf("Created subteam %v %v", teamID, teamName)
39	}
40
41	maxUses, err := keybase1.NewTeamInviteFiniteUses(3)
42	require.NoError(t, err)
43	etime := keybase1.ToUnixTime(time.Now().Add(24 * time.Hour))
44	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
45		Teamname: teamName.String(),
46		Role:     keybase1.TeamRole_WRITER,
47		MaxUses:  maxUses,
48		Etime:    &etime,
49	})
50	require.NoError(t, err)
51
52	t.Logf("Created token %v", link)
53
54	details := alice.teamGetDetails(teamName.String())
55	require.Len(t, details.AnnotatedActiveInvites, 1)
56	for _, aInvite := range details.AnnotatedActiveInvites {
57		invite := aInvite.InviteMetadata.Invite
58		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
59		tic, err := invite.Type.C()
60		require.NoError(t, err)
61		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
62	}
63
64	bob.kickTeamRekeyd()
65	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
66		Token: string(link.Ikey),
67	})
68	require.NoError(t, err)
69
70	t.Logf("User used token, waiting for rekeyd")
71
72	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))
73
74	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G, teamName.String(), false /* public */, true /* needAdmin */)
75	require.NoError(t, err)
76
77	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
78	require.NoError(t, err)
79	require.Equal(t, role, keybase1.TeamRole_WRITER)
80}
81
82func TestTeamInviteLinkAfterLeave(t *testing.T) {
83	tt := newTeamTester(t)
84	defer tt.cleanup()
85
86	alice := tt.addUser("ali")
87	bob := tt.addUser("bob")
88
89	teamID, teamName := alice.createTeam2()
90	maxUses, err := keybase1.NewTeamInviteFiniteUses(100)
91	require.NoError(t, err)
92	etime := keybase1.ToUnixTime(time.Now().AddDate(1, 0, 0))
93	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
94		Teamname: teamName.String(),
95		Role:     keybase1.TeamRole_WRITER,
96		MaxUses:  maxUses,
97		Etime:    &etime,
98	})
99	require.NoError(t, err)
100
101	t.Logf("Created team invite link: %#v", link)
102
103	bob.kickTeamRekeyd()
104	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
105		Token: string(link.Ikey),
106	})
107	require.NoError(t, err)
108
109	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))
110
111	// Bob leaves.
112	bob.leave(teamName.String())
113
114	// Make sure Bob gets different akey when accepting again, and that Alice
115	// doesn't hit the "invite link was accepted before last change membership"
116	// when handling seitan.
117	clock := clockwork.NewFakeClockAt(time.Now())
118	clock.Advance(1 * time.Second)
119	bob.tc.G.SetClock(clock)
120	alice.tc.G.SetClock(clock)
121
122	// Bob accepts the same invite again.
123	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
124		Token: string(link.Ikey),
125	})
126	require.NoError(t, err)
127
128	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(5))
129
130	t.Logf("removing bob; expecting to ban since he was added by invitelink most recently")
131	alice.removeTeamMember(teamName.String(), bob.username)
132	t.Logf("bob tries to rejoin")
133	clock.Advance(1 * time.Second)
134	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
135		Token: string(link.Ikey),
136	})
137	require.Error(t, err, "server won't let bob back in")
138	appErr, ok := err.(libkb.AppStatusError)
139	require.True(t, ok, "got an app err")
140	require.Equal(t, appErr.Code, libkb.SCTeamBanned)
141
142	t.Logf("alice adds/removes manually to clear ban")
143	alice.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_WRITER)
144	alice.removeTeamMember(teamName.String(), bob.username)
145
146	clock.Advance(1 * time.Second)
147	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
148		Token: string(link.Ikey),
149	})
150	require.NoError(t, err, "bob can rejoin")
151	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(9))
152	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G,
153		teamName.String(), false /* public */, true /* needAdmin */)
154	require.NoError(t, err)
155	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
156	require.NoError(t, err)
157	require.Equal(t, role, keybase1.TeamRole_WRITER)
158}
159
160func TestCreateSeitanInvitelinkWithDuration(t *testing.T) {
161	// Test for the GUI RPC.
162
163	tt := newTeamTester(t)
164	defer tt.cleanup()
165
166	alice := tt.addUser("ali")
167	_, teamName := alice.createTeam2()
168
169	now := alice.tc.G.Clock().Now()
170
171	maxUses := keybase1.TeamMaxUsesInfinite
172	expireAfter := "10 Y"
173	_, err := alice.teamsClient.TeamCreateSeitanInvitelinkWithDuration(
174		context.TODO(),
175		keybase1.TeamCreateSeitanInvitelinkWithDurationArg{
176			Teamname:    teamName.String(),
177			Role:        keybase1.TeamRole_WRITER,
178			MaxUses:     maxUses,
179			ExpireAfter: &expireAfter,
180		})
181	require.NoError(t, err)
182
183	details := alice.teamGetDetails(teamName.String())
184	require.Len(t, details.AnnotatedActiveInvites, 1)
185	for _, aInvite := range details.AnnotatedActiveInvites {
186		invite := aInvite.InviteMetadata.Invite
187		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
188		require.NotNil(t, invite.MaxUses)
189		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
190		require.NotNil(t, invite.Etime)
191		require.Equal(t, now.Year()+10, invite.Etime.Time().Year())
192		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
193		tic, err := invite.Type.C()
194		require.NoError(t, err)
195		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
196	}
197}
198