github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/systests/phone_number_test.go (about) 1 package systests 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 "github.com/keybase/client/go/client" 9 "github.com/keybase/client/go/emails" 10 "github.com/keybase/client/go/kbtest" 11 "github.com/keybase/client/go/libkb" 12 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestImpTeamWithPhoneNumber(t *testing.T) { 17 tt := newTeamTester(t) 18 defer tt.cleanup() 19 20 ann := tt.addUser("ann") 21 22 phone := kbtest.GenerateTestPhoneNumber() 23 impteamName := fmt.Sprintf("%s@phone,%s", phone, ann.username) 24 teamID, err := ann.lookupImplicitTeam(true /* create */, impteamName, false /* public */) 25 require.NoError(t, err) 26 27 t.Logf("Created team %s -> %s", impteamName, teamID) 28 29 teamObj := ann.loadTeamByID(teamID, true /* admin */) 30 require.Equal(t, 1, teamObj.NumActiveInvites()) 31 var invite keybase1.TeamInvite 32 for _, invite = range teamObj.GetActiveAndObsoleteInvites() { 33 // Get first invite to local var 34 } 35 require.EqualValues(t, phone, invite.Name) 36 invCat, err := invite.Type.C() 37 require.NoError(t, err) 38 require.Equal(t, keybase1.TeamInviteCategory_PHONE, invCat) 39 40 name, err := teamObj.ImplicitTeamDisplayName(context.Background()) 41 require.NoError(t, err) 42 require.Len(t, name.Writers.KeybaseUsers, 1) 43 require.Len(t, name.Writers.UnresolvedUsers, 1) 44 require.Equal(t, impteamName, name.String()) 45 } 46 47 func TestResolvePhoneToUser(t *testing.T) { 48 tt := newTeamTester(t) 49 defer tt.cleanup() 50 51 ann := tt.addUser("ann") 52 bob := tt.addUser("bob") 53 tt.logUserNames() 54 55 phone := kbtest.GenerateTestPhoneNumber() 56 57 assertion := fmt.Sprintf("%s@phone", phone) 58 for _, u := range tt.users { 59 _, res, err := u.tc.G.Resolver.ResolveUser(u.MetaContext(), assertion) 60 require.Error(t, err) 61 require.IsType(t, libkb.ResolutionError{}, err) 62 require.Contains(t, err.Error(), assertion) 63 require.Contains(t, err.Error(), "No resolution found") 64 require.Empty(t, res.GetUID()) 65 require.Empty(t, res.GetUsername()) 66 } 67 68 cli := &client.CmdAddPhoneNumber{ 69 Contextified: libkb.NewContextified(bob.tc.G), 70 PhoneNumber: "+" + phone, 71 } 72 err := cli.Run() 73 require.NoError(t, err) 74 75 code, err := kbtest.GetPhoneVerificationCode(bob.MetaContext(), keybase1.PhoneNumber("+"+phone)) 76 require.NoError(t, err) 77 78 cli2 := &client.CmdVerifyPhoneNumber{ 79 Contextified: libkb.NewContextified(bob.tc.G), 80 PhoneNumber: "+" + phone, 81 Code: code, 82 } 83 err = cli2.Run() 84 require.NoError(t, err) 85 86 cli3 := &client.CmdSetVisibilityPhoneNumber{ 87 Contextified: libkb.NewContextified(bob.tc.G), 88 PhoneNumber: "+" + phone, 89 Visibility: keybase1.IdentityVisibility_PUBLIC, 90 } 91 err = cli3.Run() 92 require.NoError(t, err) 93 94 for _, u := range tt.users { 95 usr, res, err := u.tc.G.Resolver.ResolveUser(u.MetaContext(), assertion) 96 require.NoError(t, err) 97 require.Equal(t, bob.username, res.GetUsername()) 98 require.Equal(t, bob.username, usr.Username) 99 require.Equal(t, bob.uid, res.GetUID()) 100 require.Equal(t, bob.uid, usr.Uid) 101 require.True(t, res.IsServerTrust()) 102 } 103 104 // Try to create impteam with now-resolvable phone number. 105 impteamName := fmt.Sprintf("%s,%s", assertion, ann.username) 106 lookupRes, err := ann.lookupImplicitTeam2(true /* create */, impteamName, false /* public */) 107 require.NoError(t, err) 108 require.Equal(t, fmt.Sprintf("%s,%s", ann.username, bob.username), lookupRes.DisplayName.String()) 109 } 110 111 func TestServerTrustResolveInvalidInput(t *testing.T) { 112 tt := newTeamTester(t) 113 defer tt.cleanup() 114 115 ann := tt.addUser("ann") 116 117 checkErr := func(err error) { 118 require.Error(t, err) 119 require.IsType(t, libkb.ResolutionError{}, err) 120 resErr := err.(libkb.ResolutionError) 121 require.Equal(t, libkb.ResolutionErrorInvalidInput, resErr.Kind) 122 } 123 124 _, _, err := ann.tc.G.Resolver.ResolveUser(ann.MetaContext(), "111@phone") 125 checkErr(err) 126 _, _, err = ann.tc.G.Resolver.ResolveUser(ann.MetaContext(), "[notvalid@x]@email") 127 checkErr(err) 128 } 129 130 type mockPhoneNotification struct { 131 list []keybase1.UserPhoneNumber 132 category string 133 phoneNumber keybase1.PhoneNumber 134 } 135 136 type mockPhoneListener struct { 137 libkb.NoopNotifyListener 138 notifications []mockPhoneNotification 139 } 140 141 var _ libkb.NotifyListener = (*mockPhoneListener)(nil) 142 143 func (n *mockPhoneListener) PhoneNumbersChanged(list []keybase1.UserPhoneNumber, category string, phoneNumber keybase1.PhoneNumber) { 144 n.notifications = append(n.notifications, mockPhoneNotification{ 145 list, category, phoneNumber, 146 }) 147 } 148 149 func (n *mockPhoneListener) DrainPhoneNumberNotifications() (ret []mockPhoneNotification) { 150 ret = n.notifications 151 n.notifications = nil 152 return ret 153 } 154 155 func setupUserWithMockPhoneListener(user *userPlusDevice) *mockPhoneListener { 156 userListener := &mockPhoneListener{} 157 user.tc.G.SetService() 158 user.tc.G.NotifyRouter.AddListener(userListener) 159 return userListener 160 } 161 162 func TestPhoneNumberNotifications(t *testing.T) { 163 tt := newTeamTester(t) 164 defer tt.cleanup() 165 166 ann := tt.addUser("ann") 167 annListener := setupUserWithMockPhoneListener(ann) 168 bob := tt.addUser("bob") 169 bobListener := setupUserWithMockPhoneListener(bob) 170 tt.logUserNames() 171 172 phone := "+" + kbtest.GenerateTestPhoneNumber() 173 kbPhone := keybase1.PhoneNumber(phone) 174 cli := &client.CmdAddPhoneNumber{ 175 Contextified: libkb.NewContextified(ann.tc.G), 176 PhoneNumber: phone, 177 } 178 err := cli.Run() 179 require.NoError(t, err) 180 181 assertNotificationGetList := func(listener *mockPhoneListener, phoneNumber keybase1.PhoneNumber, category string) []keybase1.UserPhoneNumber { 182 notifications := listener.DrainPhoneNumberNotifications() 183 require.Len(t, notifications, 1) 184 require.Equal(t, kbPhone, notifications[0].phoneNumber) 185 require.Equal(t, category, notifications[0].category) 186 return notifications[0].list 187 } 188 189 // adding the phone number generates a notification 190 ann.drainGregor() 191 list := assertNotificationGetList(annListener, kbPhone, "phone.added") 192 require.Len(t, list, 1) 193 require.Equal(t, kbPhone, list[0].PhoneNumber) 194 require.False(t, list[0].Verified) 195 require.False(t, list[0].Superseded) 196 197 // verifying the phone number generates a notification 198 code, err := kbtest.GetPhoneVerificationCode(ann.MetaContext(), kbPhone) 199 require.NoError(t, err) 200 cli2 := &client.CmdVerifyPhoneNumber{ 201 Contextified: libkb.NewContextified(ann.tc.G), 202 PhoneNumber: phone, 203 Code: code, 204 } 205 err = cli2.Run() 206 require.NoError(t, err) 207 ann.drainGregor() 208 list = assertNotificationGetList(annListener, kbPhone, "phone.verified") 209 require.Len(t, list, 1) 210 require.Equal(t, kbPhone, list[0].PhoneNumber) 211 require.True(t, list[0].Verified) 212 require.False(t, list[0].Superseded) 213 214 // if bob now adds and verifies that same number, he should have new notifications for add and verify 215 // and ann should have one that her number was superseded 216 cli3 := &client.CmdAddPhoneNumber{ 217 Contextified: libkb.NewContextified(bob.tc.G), 218 PhoneNumber: phone, 219 } 220 err = cli3.Run() 221 require.NoError(t, err) 222 bob.drainGregor() 223 list = assertNotificationGetList(bobListener, kbPhone, "phone.added") 224 require.Len(t, list, 1) 225 code, err = kbtest.GetPhoneVerificationCode(bob.MetaContext(), kbPhone) 226 require.NoError(t, err) 227 cli4 := &client.CmdVerifyPhoneNumber{ 228 Contextified: libkb.NewContextified(bob.tc.G), 229 PhoneNumber: phone, 230 Code: code, 231 } 232 err = cli4.Run() 233 require.NoError(t, err) 234 235 bob.drainGregor() 236 list = assertNotificationGetList(bobListener, kbPhone, "phone.verified") 237 require.Len(t, list, 1) 238 require.Equal(t, kbPhone, list[0].PhoneNumber) 239 require.True(t, list[0].Verified) 240 require.False(t, list[0].Superseded) 241 242 ann.drainGregor() 243 list = assertNotificationGetList(annListener, kbPhone, "phone.superseded") 244 require.Len(t, list, 1) 245 require.Equal(t, kbPhone, list[0].PhoneNumber) 246 require.True(t, list[0].Verified) 247 require.True(t, list[0].Superseded) 248 } 249 250 func TestImplicitTeamWithEmail(t *testing.T) { 251 tt := newTeamTester(t) 252 defer tt.cleanup() 253 254 ann := tt.addUser("ann") 255 bob := tt.addUser("bob") 256 257 email := bob.userInfo.email 258 assertion := fmt.Sprintf("[%s]@email", email) 259 260 impteamName := fmt.Sprintf("%s,%s", ann.username, assertion) 261 teamID, err := ann.lookupImplicitTeam(true /* create */, impteamName, false /* public */) 262 require.NoError(t, err) 263 264 teamObj := ann.loadTeamByID(teamID, true /* admin */) 265 require.Equal(t, 1, teamObj.NumActiveInvites()) 266 var invite keybase1.TeamInvite 267 for _, invite = range teamObj.GetActiveAndObsoleteInvites() { 268 // Get first invite to local var 269 } 270 require.EqualValues(t, email, invite.Name) 271 invCat, err := invite.Type.C() 272 require.NoError(t, err) 273 require.Equal(t, keybase1.TeamInviteCategory_EMAIL, invCat) 274 275 name, err := teamObj.ImplicitTeamDisplayName(context.Background()) 276 require.NoError(t, err) 277 require.Len(t, name.Writers.KeybaseUsers, 1) 278 require.Len(t, name.Writers.UnresolvedUsers, 1) 279 require.Equal(t, fmt.Sprintf("%s,%s", assertion, ann.username), name.String()) 280 281 t.Logf("Got display name back: %q", name.String()) 282 283 seqnoAfterResolve := teamObj.NextSeqno() 284 285 // Verifying an email should RSVP the invitation which will notify 286 // (using SBS gregor msg) ann to resolve it. 287 err = kbtest.VerifyEmailAuto(bob.MetaContext(), keybase1.EmailAddress(email)) 288 require.NoError(t, err) 289 err = emails.SetVisibilityEmail(bob.MetaContext(), keybase1.EmailAddress(email), keybase1.IdentityVisibility_PUBLIC) 290 require.NoError(t, err) 291 292 ann.pollForTeamSeqnoLinkWithLoadArgs(keybase1.LoadTeamArg{ID: teamID}, seqnoAfterResolve) 293 }