1package systests
2
3import (
4	"strings"
5	"testing"
6
7	"golang.org/x/net/context"
8
9	"github.com/keybase/client/go/client"
10	"github.com/keybase/client/go/libkb"
11	keybase1 "github.com/keybase/client/go/protocol/keybase1"
12	"github.com/keybase/client/go/teams"
13	"github.com/stretchr/testify/require"
14)
15
16func TestTeamOpenAutoAddMember(t *testing.T) {
17	tt := newTeamTester(t)
18	defer tt.cleanup()
19
20	own := tt.addUser("own")
21	roo := tt.addUser("roo")
22
23	teamName, err := libkb.RandString("tt", 5)
24	require.NoError(t, err)
25	teamName = strings.ToLower(teamName)
26
27	cli := own.teamsClient
28	createRes, err := cli.TeamCreateWithSettings(context.TODO(), keybase1.TeamCreateWithSettingsArg{
29		Name: teamName,
30		Settings: keybase1.TeamSettings{
31			Open:   true,
32			JoinAs: keybase1.TeamRole_READER,
33		},
34	})
35	require.NoError(t, err)
36	teamID := createRes.TeamID
37
38	t.Logf("Open team name is %q", teamName)
39
40	roo.kickTeamRekeyd()
41	ret, err := roo.teamsClient.TeamRequestAccess(context.TODO(), keybase1.TeamRequestAccessArg{Name: teamName})
42	require.NoError(t, err)
43	require.Equal(t, true, ret.Open)
44
45	own.waitForTeamChangedGregor(teamID, keybase1.Seqno(2))
46
47	teamObj, err := teams.Load(context.TODO(), own.tc.G, keybase1.LoadTeamArg{
48		Name:        teamName,
49		ForceRepoll: true,
50	})
51	require.NoError(t, err)
52
53	role, err := teamObj.MemberRole(context.TODO(), roo.userVersion())
54	require.NoError(t, err)
55	require.Equal(t, role, keybase1.TeamRole_READER)
56}
57
58func TestTeamOpenSettings(t *testing.T) {
59	tt := newTeamTester(t)
60	defer tt.cleanup()
61
62	own := tt.addUser("own")
63
64	id, teamName := own.createTeam2()
65	t.Logf("Open team name is %q", teamName)
66
67	loadTeam := func() *teams.Team {
68		ret, err := teams.Load(context.TODO(), own.tc.G, keybase1.LoadTeamArg{
69			Name:        teamName.String(),
70			ForceRepoll: true,
71		})
72		require.NoError(t, err)
73		return ret
74	}
75
76	teamObj := loadTeam()
77	require.Equal(t, teamObj.IsOpen(), false)
78
79	err := teams.ChangeTeamSettingsByID(context.TODO(), own.tc.G, id, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_READER})
80	require.NoError(t, err)
81
82	teamObj = loadTeam()
83	require.Equal(t, teamObj.IsOpen(), true)
84
85	err = teams.ChangeTeamSettingsByID(context.TODO(), own.tc.G, id, keybase1.TeamSettings{Open: false})
86	require.NoError(t, err)
87
88	teamObj = loadTeam()
89	require.Equal(t, teamObj.IsOpen(), false)
90}
91
92func TestOpenSubteamAdd(t *testing.T) {
93	tt := newTeamTester(t)
94	defer tt.cleanup()
95
96	own := tt.addUser("own")
97	roo := tt.addUser("roo")
98
99	// Creating team, subteam, sending open setting, checking if it's set.
100
101	team := own.createTeam()
102
103	parentName, err := keybase1.TeamNameFromString(team)
104	require.NoError(t, err)
105
106	subteam, err := teams.CreateSubteam(context.TODO(), own.tc.G, "zzz", parentName, keybase1.TeamRole_NONE /* addSelfAs */)
107	require.NoError(t, err)
108
109	t.Logf("Open team name is %q, subteam is %q", team, subteam)
110
111	subteamObj, err := teams.Load(context.TODO(), own.tc.G, keybase1.LoadTeamArg{
112		ID:          *subteam,
113		ForceRepoll: true,
114	})
115	require.NoError(t, err)
116
117	err = teams.ChangeTeamSettingsByID(context.TODO(), own.tc.G, subteamObj.ID, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_READER})
118	require.NoError(t, err)
119
120	subteamObj, err = teams.Load(context.TODO(), own.tc.G, keybase1.LoadTeamArg{
121		ID:          *subteam,
122		ForceRepoll: true,
123	})
124	require.NoError(t, err)
125	require.Equal(t, subteamObj.IsOpen(), true)
126
127	// Kick rekeyd so team request notifications come quicker.
128	roo.kickTeamRekeyd()
129
130	// User requesting access
131	subteamNameStr := subteamObj.Name().String()
132	_, err = roo.teamsClient.TeamRequestAccess(context.TODO(), keybase1.TeamRequestAccessArg{Name: subteamNameStr})
133	require.NoError(t, err)
134
135	own.waitForTeamChangedGregor(*subteam, keybase1.Seqno(3))
136
137	subteamObj, err = teams.Load(context.TODO(), own.tc.G, keybase1.LoadTeamArg{
138		ID:          *subteam,
139		ForceRepoll: true,
140	})
141	require.NoError(t, err)
142
143	role, err := subteamObj.MemberRole(context.TODO(), roo.userVersion())
144	require.NoError(t, err)
145	require.Equal(t, role, keybase1.TeamRole_READER)
146}
147
148func TestTeamOpenMultipleTars(t *testing.T) {
149	tt := newTeamTester(t)
150	defer tt.cleanup()
151
152	tar1 := tt.addUser("roo1")
153	tar2 := tt.addUser("roo2")
154	tar3 := tt.addUser("roo3")
155	own := tt.addUser("own")
156	tt.logUserNames()
157
158	teamID, teamName := own.createTeam2()
159	t.Logf("Open team name is %q", teamName.String())
160
161	// Everyone requests access before team is open.
162	_, err := tar1.teamsClient.TeamRequestAccess(context.Background(), keybase1.TeamRequestAccessArg{Name: teamName.String()})
163	require.NoError(t, err)
164	_, err = tar2.teamsClient.TeamRequestAccess(context.Background(), keybase1.TeamRequestAccessArg{Name: teamName.String()})
165	require.NoError(t, err)
166	_, err = tar3.teamsClient.TeamRequestAccess(context.Background(), keybase1.TeamRequestAccessArg{Name: teamName.String()})
167	require.NoError(t, err)
168
169	// Change settings to open.
170	tar3.kickTeamRekeyd()
171	err = teams.ChangeTeamSettingsByID(context.Background(), own.tc.G, teamID, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_READER})
172	require.NoError(t, err)
173
174	own.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))
175
176	teamObj, err := teams.Load(context.Background(), own.tc.G, keybase1.LoadTeamArg{
177		Name:        teamName.String(),
178		ForceRepoll: true,
179	})
180	require.NoError(t, err)
181
182	for i := 0; i < 3; i++ {
183		role, err := teamObj.MemberRole(context.Background(), tt.users[i].userVersion())
184		require.NoError(t, err)
185		require.Equal(t, role, keybase1.TeamRole_READER)
186	}
187}
188
189func TestTeamOpenBans(t *testing.T) {
190	tt := newTeamTester(t)
191	defer tt.cleanup()
192
193	own := tt.addUser("own")
194	bob := tt.addUser("bob")
195
196	team := own.createTeam()
197	t.Logf("Open team name is %q", team)
198
199	teamName, err := keybase1.TeamNameFromString(team)
200	require.NoError(t, err)
201
202	t.Logf("Trying team edit cli...")
203	runner := client.NewCmdTeamSettingsRunner(own.tc.G)
204	runner.Team = teamName
205	joinAsRole := keybase1.TeamRole_READER
206	runner.JoinAsRole = &joinAsRole
207	err = runner.Run()
208	require.NoError(t, err)
209
210	own.addTeamMember(team, bob.username, keybase1.TeamRole_READER)
211
212	removeRunner := client.NewCmdTeamRemoveMemberRunner(own.tc.G)
213	removeRunner.Team = team
214	removeRunner.Assertion = bob.username
215	removeRunner.Force = true
216	err = removeRunner.Run()
217	require.NoError(t, err)
218
219	_, err = bob.teamsClient.TeamRequestAccess(context.TODO(), keybase1.TeamRequestAccessArg{Name: team})
220	require.Error(t, err)
221	appErr, ok := err.(libkb.AppStatusError)
222	require.True(t, ok)
223	require.Equal(t, appErr.Code, libkb.SCTeamBanned)
224}
225
226func TestTeamOpenPuklessRequest(t *testing.T) {
227	tt := newTeamTester(t)
228	defer tt.cleanup()
229
230	own := tt.addUser("own")
231	bob := tt.addPuklessUser("bob")
232
233	teamID, teamName := own.createTeam2()
234	team := teamName.String()
235	t.Logf("Open team name is %q", team)
236
237	err := teams.ChangeTeamSettingsByID(context.Background(), own.tc.G, teamID, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_WRITER})
238	require.NoError(t, err)
239
240	// Bob is PUKless but he can still request access. But he will
241	// only be keyed in when he gets a PUK.
242	_, err = bob.teamsClient.TeamRequestAccess(context.Background(), keybase1.TeamRequestAccessArg{Name: team})
243	require.NoError(t, err)
244
245	_, err = bob.teamsClient.TeamRequestAccess(context.Background(), keybase1.TeamRequestAccessArg{Name: team})
246	require.Error(t, err)
247	t.Logf("Doubled TeamRequestAccess error is: %v", err)
248
249	// Upgrading to PUK should trigger team_rekeyd and adding bob to
250	// team by an admin.
251	bob.kickTeamRekeyd()
252	bob.perUserKeyUpgrade()
253
254	own.pollForTeamSeqnoLink(team, keybase1.Seqno(3))
255
256	teamObj := own.loadTeam(team, true /* admin */)
257	members, err := teamObj.Members()
258	require.NoError(t, err)
259	require.Equal(t, 2, len(members.AllUIDs())) // just owner
260	role, err := teamObj.MemberRole(context.Background(), bob.userVersion())
261	require.NoError(t, err)
262	require.Equal(t, keybase1.TeamRole_WRITER, role)
263}
264
265// Consider user that resets their account and tries to re-join.
266func TestTeamOpenResetAndRejoin(t *testing.T) {
267	tt := newTeamTester(t)
268	defer tt.cleanup()
269
270	ann := tt.addUser("ann")
271	bob := tt.addUser("bob")
272	tt.logUserNames()
273
274	teamID, teamName := ann.createTeam2()
275	team := teamName.String()
276	ann.addTeamMember(team, bob.username, keybase1.TeamRole_WRITER)
277	err := teams.ChangeTeamSettingsByID(context.Background(), ann.tc.G, teamID, keybase1.TeamSettings{Open: true, JoinAs: keybase1.TeamRole_READER})
278	require.NoError(t, err)
279
280	t.Logf("Open team name is %q", team)
281
282	bob.kickTeamRekeyd()
283	bob.reset()
284
285	// Wait for change membership link after bob resets
286	ann.pollForTeamSeqnoLink(team, keybase1.Seqno(4))
287
288	bob.loginAfterResetPukless()
289	_, err = bob.teamsClient.TeamRequestAccess(context.Background(), keybase1.TeamRequestAccessArg{Name: team})
290	require.NoError(t, err)
291
292	bob.kickTeamRekeyd()
293	bob.perUserKeyUpgrade()
294
295	// Poll for change_membership after bob's TAR gets acted on.
296	ann.pollForTeamSeqnoLink(team, keybase1.Seqno(5))
297
298	teamObj := ann.loadTeam(team, true /* admin */)
299
300	require.Len(t, teamObj.GetActiveAndObsoleteInvites(), 0)
301
302	members, err := teamObj.Members()
303	require.NoError(t, err)
304	require.Len(t, members.AllUIDs(), 2)
305	role, err := teamObj.MemberRole(context.Background(), bob.userVersion())
306	require.NoError(t, err)
307	require.Equal(t, keybase1.TeamRole_READER, role)
308}
309