1package systests
2
3import (
4	"testing"
5
6	"github.com/keybase/client/go/teams"
7
8	"github.com/keybase/client/go/emails"
9
10	"github.com/keybase/client/go/externals"
11	"github.com/keybase/client/go/kbtest"
12	"github.com/keybase/client/go/libkb"
13	"github.com/keybase/client/go/phonenumbers"
14	keybase1 "github.com/keybase/client/go/protocol/keybase1"
15	"github.com/stretchr/testify/require"
16	context "golang.org/x/net/context"
17)
18
19func assertionFromKV(t *testing.T, key, value string) libkb.AssertionURL {
20	actx := externals.MakeStaticAssertionContext(context.TODO())
21	ret, err := libkb.ParseAssertionURLKeyValue(actx, key, value, true /* strict */)
22	require.NoError(t, err)
23	return ret
24}
25
26// Same SBS test can run against different SBS types, where each one has
27// different way of proving (verifying), revoking etc. Encapsulate all that
28// under a type that implements `userSBSProvider` and pass it to the "generic"
29// SBS test function.
30type userSBSProvider interface {
31	SetUser(user *userPlusDevice)
32	GetAssertionKV() (key string, value string)
33	Verify()
34	Revoke()
35}
36
37// Phone numbers
38type userSBSPhoneNumber struct {
39	u           *userPlusDevice
40	phoneNumber string //without `+`
41}
42
43func (p *userSBSPhoneNumber) SetUser(user *userPlusDevice) {
44	p.u = user
45	p.phoneNumber = kbtest.GenerateTestPhoneNumber()
46}
47
48func (p *userSBSPhoneNumber) GetAssertionKV() (key string, value string) {
49	return "phone", p.phoneNumber
50}
51
52func (p *userSBSPhoneNumber) Verify() {
53	mctx := p.u.MetaContext()
54	tctx := p.u.tc
55	phoneNumber := keybase1.PhoneNumber("+" + p.phoneNumber)
56	require.NoError(tctx.T, phonenumbers.AddPhoneNumber(mctx, phoneNumber, keybase1.IdentityVisibility_PUBLIC))
57	code, err := kbtest.GetPhoneVerificationCode(libkb.NewMetaContextTODO(tctx.G), phoneNumber)
58	require.NoError(tctx.T, err)
59	require.NoError(tctx.T, phonenumbers.VerifyPhoneNumber(mctx, phoneNumber, code))
60}
61
62func (p *userSBSPhoneNumber) Revoke() {
63	err := phonenumbers.DeletePhoneNumber(p.u.MetaContext(), keybase1.PhoneNumber("+"+p.phoneNumber))
64	require.NoError(p.u.tc.T, err)
65}
66
67// ------------------
68
69// Emails
70type userSBSEmail struct {
71	u *userPlusDevice
72}
73
74func (p *userSBSEmail) SetUser(user *userPlusDevice) {
75	p.u = user
76}
77
78func (p *userSBSEmail) GetAssertionKV() (key string, value string) {
79	return "email", p.u.userInfo.email
80}
81
82func (p *userSBSEmail) Verify() {
83	emailAddress := keybase1.EmailAddress(p.u.userInfo.email)
84	err := emails.SetVisibilityEmail(p.u.MetaContext(), emailAddress, keybase1.IdentityVisibility_PUBLIC)
85	require.NoError(p.u.tc.T, err)
86	err = kbtest.VerifyEmailAuto(p.u.MetaContext(), emailAddress)
87	require.NoError(p.u.tc.T, err)
88}
89
90func (p *userSBSEmail) Revoke() {
91	err := emails.DeleteEmail(p.u.MetaContext(), keybase1.EmailAddress(p.u.userInfo.email))
92	require.NoError(p.u.tc.T, err)
93}
94
95// ------------------
96
97// Rooter
98type userSBSRooter struct {
99	u *userPlusDevice
100}
101
102func (p *userSBSRooter) SetUser(user *userPlusDevice) {
103	p.u = user
104}
105
106func (p *userSBSRooter) GetAssertionKV() (key string, value string) {
107	return "rooter", p.u.username
108}
109
110func (p *userSBSRooter) Verify() {
111	p.u.proveRooter()
112}
113
114func (p *userSBSRooter) Revoke() {
115	p.u.revokeServiceProof("rooter")
116}
117
118// ------------------
119
120func testTeamInviteSBS(t *testing.T, sbs userSBSProvider) {
121	tt := newTeamTester(t)
122	defer tt.cleanup()
123
124	ann := tt.addUser("ann")
125	bob := tt.addUser("bob")
126	sbs.SetUser(bob)
127
128	// User 0 creates a team.
129	teamID, teamName := ann.createTeam2()
130
131	key, value := sbs.GetAssertionKV()
132	assertionURL := assertionFromKV(t, key, value)
133	assertion := assertionURL.String()
134
135	ann.addTeamMember(teamName.String(), assertion, keybase1.TeamRole_WRITER)
136
137	ann.kickTeamRekeyd()
138	sbs.Verify()
139
140	ann.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))
141	bob.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))
142
143	// The team should have user 1 in it now as a writer.
144	t0 := ann.loadTeam(teamName.String(), true /* admin */)
145	writers, err := t0.UsersWithRole(keybase1.TeamRole_WRITER)
146	require.NoError(t, err)
147	require.Len(t, writers, 1)
148	require.Equal(t, bob.uid, writers[0].Uid)
149
150	// The invite should not be in the active invite map.
151	require.Equal(t, 0, t0.NumActiveInvites())
152	exists, err := t0.HasActiveInvite(tt.users[0].tc.MetaContext(), keybase1.TeamInviteName(value), key)
153	require.NoError(t, err)
154	require.False(t, exists, "after accepting invite, active invite shouldn't exist")
155}
156
157func TestTeamInviteSBSPhone(t *testing.T) {
158	testTeamInviteSBS(t, &userSBSPhoneNumber{})
159}
160
161func TestTeamInviteSBSEmail(t *testing.T) {
162	testTeamInviteSBS(t, &userSBSEmail{})
163}
164
165func TestTeamInviteSBSRooter(t *testing.T) {
166	testTeamInviteSBS(t, &userSBSRooter{})
167}
168
169// ------------------
170
171func testTeamInviteExistingUserSBS(t *testing.T, sbs userSBSProvider) {
172	tt := newTeamTester(t)
173	defer tt.cleanup()
174
175	ann := tt.addUser("ann")
176	bob := tt.addUser("bob")
177	sbs.SetUser(bob)
178
179	key, value := sbs.GetAssertionKV()
180	assertionURL := assertionFromKV(t, key, value)
181	assertion := assertionURL.String()
182
183	sbs.Verify()
184
185	// User 0 creates a team.
186	_, teamName := ann.createTeam2()
187
188	// Add bob by SBS assertion. Should just add bob and not an invite. Adding
189	// resolvable SBS assertion via invite would also bounce off the server
190	// with `TEAM_INVITE_USER_EXISTS` error.
191	ann.addTeamMember(teamName.String(), assertion, keybase1.TeamRole_WRITER)
192
193	// The team should have user 1 in it now as a writer.
194	t0 := ann.loadTeam(teamName.String(), true /* admin */)
195	writers, err := t0.UsersWithRole(keybase1.TeamRole_WRITER)
196	require.NoError(t, err)
197	require.Len(t, writers, 1)
198	require.Equal(t, bob.uid, writers[0].Uid)
199
200	// There should be no invite for the SBS.
201	require.Equal(t, 0, t0.NumActiveInvites())
202	exists, err := t0.HasActiveInvite(tt.users[0].tc.MetaContext(), keybase1.TeamInviteName(value), key)
203	require.NoError(t, err)
204	require.False(t, exists, "after adding resolvable assertion, no invite should have been created")
205}
206
207func TestTeamInviteExistingUserSBSPhone(t *testing.T) {
208	testTeamInviteExistingUserSBS(t, &userSBSPhoneNumber{})
209}
210
211func TestTeamInviteExistingUserSBSEmail(t *testing.T) {
212	testTeamInviteExistingUserSBS(t, &userSBSEmail{})
213}
214
215func TestTeamInviteExistingUserSBSRooter(t *testing.T) {
216	testTeamInviteExistingUserSBS(t, &userSBSRooter{})
217}
218
219// ------------------
220
221func TestTeamInviteSBSError(t *testing.T) {
222	// Make sure we can't add invites for assertions if we can't attempt to
223	// resolve them.
224
225	tt := newTeamTester(t)
226	defer tt.cleanup()
227
228	ann := tt.addUser("ann")
229	bob := tt.addUser("bob")
230
231	ann.disableTOFUSearch()
232
233	teamID, teamName := ann.createTeam2()
234
235	sbsProviders := []userSBSProvider{
236		&userSBSEmail{},
237		&userSBSPhoneNumber{},
238	}
239
240	for _, sbs := range sbsProviders {
241		sbs.SetUser(bob)
242		sbs.Verify()
243
244		key, value := sbs.GetAssertionKV()
245		assertionURL := assertionFromKV(t, key, value)
246		assertion := assertionURL.String()
247
248		_, err := teams.AddMemberByID(context.TODO(), ann.tc.G, teamID, assertion, keybase1.TeamRole_WRITER, nil, nil /* emailInviteMsg */)
249		require.Error(t, err)
250		require.Contains(t, err.Error(), "error 602") // user cannot search for assertions
251	}
252
253	t0 := ann.loadTeam(teamName.String(), true /* admin */)
254	require.Equal(t, 0, t0.NumActiveInvites())
255}
256