github.com/keybase/client/go@v0.0.0-20240520164431-4f512a4c85a3/teams/implicit_test.go (about) 1 package teams 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "strings" 7 "testing" 8 "time" 9 10 "golang.org/x/net/context" 11 12 "github.com/davecgh/go-spew/spew" 13 "github.com/keybase/client/go/externals" 14 "github.com/keybase/client/go/kbtest" 15 "github.com/keybase/client/go/libkb" 16 "github.com/keybase/client/go/protocol/keybase1" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func newImplicitTLFID(public bool) keybase1.TLFID { 21 suffix := byte(0x29) 22 if public { 23 suffix = 0x2a 24 } 25 26 idBytes, err := libkb.RandBytesWithSuffix(16, suffix) 27 if err != nil { 28 panic("RandBytes failed: " + err.Error()) 29 } 30 return keybase1.TLFID(hex.EncodeToString(idBytes)) 31 } 32 33 func TestImplicitRaceCreateTLFs(t *testing.T) { 34 tc := SetupTest(t, "team", 1) 35 defer tc.Cleanup() 36 u, err := kbtest.CreateAndSignupFakeUser("t", tc.G) 37 require.NoError(t, err) 38 displayName := u.Username 39 _, _, _, err = LookupImplicitTeam(context.TODO(), tc.G, displayName, false, ImplicitTeamOptions{}) 40 require.Error(t, err) 41 require.IsType(t, TeamDoesNotExistError{}, err) 42 createdTeam, _, impTeamName, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, displayName, false) 43 require.NoError(t, err) 44 tlfid0 := createdTeam.LatestKBFSTLFID() 45 require.False(t, impTeamName.IsPublic) 46 require.True(t, tlfid0.IsNil()) 47 tlfid1 := newImplicitTLFID(true) 48 n := 4 49 doneCh := make(chan struct{}, n+1) 50 for i := 0; i < n; i++ { 51 go func() { 52 err = CreateTLF(context.TODO(), tc.G, keybase1.CreateTLFArg{TeamID: createdTeam.ID, TlfID: tlfid1}) 53 require.NoError(t, err) 54 doneCh <- struct{}{} 55 }() 56 } 57 for i := 0; i < n; i++ { 58 select { 59 case <-doneCh: 60 case <-time.After(time.Minute): 61 t.Fatal("failed to get racing racers back") 62 } 63 tc.G.Log.Debug("Got finisher %d", i) 64 } 65 // second time, LookupOrCreate should Lookup the team just created. 66 createdTeam2, _, impTeamName2, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, displayName, false) 67 require.NoError(t, err) 68 tlfid2 := createdTeam2.LatestKBFSTLFID() 69 require.Equal(t, createdTeam.ID, createdTeam2.ID) 70 require.Equal(t, impTeamName, impTeamName2, "public: %v", false) 71 require.Equal(t, tlfid1, tlfid2, "the right TLFID came back") 72 } 73 74 func TestLookupImplicitTeams(t *testing.T) { 75 tc := SetupTest(t, "team", 1) 76 defer tc.Cleanup() 77 78 numKBUsers := 3 79 var usernames []string 80 for i := 0; i < numKBUsers; i++ { 81 u, err := kbtest.CreateAndSignupFakeUser("t", tc.G) 82 require.NoError(t, err) 83 usernames = append(usernames, u.Username) 84 } 85 86 lookupAndCreate := func(displayName string, public bool) { 87 t.Logf("displayName:%v public:%v", displayName, public) 88 _, _, _, err := LookupImplicitTeam(context.TODO(), tc.G, displayName, public, ImplicitTeamOptions{}) 89 require.Error(t, err) 90 require.IsType(t, TeamDoesNotExistError{}, err) 91 92 createdTeam, _, impTeamName, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, displayName, 93 public) 94 require.NoError(t, err) 95 tlfid0 := createdTeam.LatestKBFSTLFID() 96 require.Equal(t, public, impTeamName.IsPublic) 97 require.True(t, tlfid0.IsNil()) 98 99 tlfid1 := newImplicitTLFID(public) 100 err = CreateTLF(context.TODO(), tc.G, keybase1.CreateTLFArg{TeamID: createdTeam.ID, TlfID: tlfid1}) 101 require.NoError(t, err) 102 103 // We can double this, and it still should work (and noop the second Time) 104 err = CreateTLF(context.TODO(), tc.G, keybase1.CreateTLFArg{TeamID: createdTeam.ID, TlfID: tlfid1}) 105 require.NoError(t, err) 106 107 // second time, LookupOrCreate should Lookup the team just created. 108 createdTeam2, _, impTeamName2, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, displayName, 109 public) 110 require.NoError(t, err) 111 tlfid2 := createdTeam2.LatestKBFSTLFID() 112 require.Equal(t, createdTeam.ID, createdTeam2.ID) 113 require.Equal(t, impTeamName, impTeamName2, "public: %v", public) 114 require.Equal(t, tlfid1, tlfid2, "the right TLFID came back") 115 116 lookupTeam, _, impTeamName, err := LookupImplicitTeam(context.TODO(), tc.G, displayName, public, ImplicitTeamOptions{}) 117 require.NoError(t, err) 118 require.Equal(t, createdTeam.ID, lookupTeam.ID) 119 120 team := createdTeam 121 teamDisplay, err := team.ImplicitTeamDisplayNameString(context.TODO()) 122 require.NoError(t, err) 123 formatName, err := FormatImplicitTeamDisplayName(context.TODO(), tc.G, impTeamName) 124 require.NoError(t, err) 125 require.Equal(t, teamDisplay, formatName) 126 require.Equal(t, team.IsPublic(), public) 127 128 expr := fmt.Sprintf("tid:%s", createdTeam.ID) 129 rres := tc.G.Resolver.ResolveFullExpressionNeedUsername(libkb.NewMetaContextForTest(tc), expr) 130 require.NoError(t, rres.GetError()) 131 require.True(t, rres.GetTeamID().Exists()) 132 } 133 134 displayName := strings.Join(usernames, ",") 135 lookupAndCreate(displayName, false) 136 lookupAndCreate(displayName, true) 137 displayName = fmt.Sprintf("mike@twitter,%s,james@github", displayName) 138 lookupAndCreate(displayName, false) 139 lookupAndCreate(displayName, true) 140 141 _, _, _, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, "dksjdskjs/sxs?", false) 142 require.Error(t, err) 143 _, _, _, err = LookupOrCreateImplicitTeam(context.TODO(), tc.G, "dksjdskjs/sxs?", true) 144 require.Error(t, err) 145 146 // Create the same team right on top of each other 147 displayName = strings.Join(usernames, ",") + ",josecanseco@twitter" 148 ch := make(chan error, 2) 149 go func() { 150 _, _, _, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, displayName, false) 151 ch <- err 152 }() 153 go func() { 154 _, _, _, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, displayName, false) 155 ch <- err 156 }() 157 require.NoError(t, <-ch) 158 require.NoError(t, <-ch) 159 } 160 161 // Test an implicit team where one user does not yet have a PUK. 162 func TestImplicitPukless(t *testing.T) { 163 fus, tcs, cleanup := setupNTestsWithPukless(t, 2, 1) 164 defer cleanup() 165 166 displayName := "" + fus[0].Username + "," + fus[1].Username 167 t.Logf("U0 creates an implicit team: %v", displayName) 168 team, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, displayName, false /*isPublic*/) 169 require.NoError(t, err) 170 171 team2, _, _, err := LookupImplicitTeam(context.Background(), tcs[0].G, displayName, false /*isPublic*/, ImplicitTeamOptions{}) 172 require.NoError(t, err) 173 require.Equal(t, team.ID, team2.ID) 174 175 team2, _, _, err = LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, displayName, false /*isPublic*/) 176 require.NoError(t, err) 177 require.Equal(t, team.ID, team2.ID) 178 179 t.Logf("U0 loads the team") 180 team, err = Load(context.Background(), tcs[0].G, keybase1.LoadTeamArg{ID: team.ID}) 181 require.NoError(t, err) 182 require.False(t, team.IsPublic()) 183 u0Role, err := team.chain().GetUserRole(fus[0].GetUserVersion()) 184 require.NoError(t, err) 185 require.Equal(t, keybase1.TeamRole_OWNER, u0Role) 186 u1Role, err := team.chain().GetUserRole(fus[1].GetUserVersion()) 187 require.True(t, err != nil || u1Role == keybase1.TeamRole_NONE, "u1 should not yet be a member") 188 t.Logf("invites: %v", spew.Sdump(team.chain().ActiveInvites())) 189 itype, err := TeamInviteTypeFromString(tcs[0].MetaContext(), "keybase") 190 require.NoError(t, err, "should be able to make invite type for 'keybase'") 191 invite, err := team.chain().FindActiveInvite(fus[1].GetUserVersion().TeamInviteName(), itype) 192 require.NoError(t, err, "team should have invite for the puk-less user") 193 require.Equal(t, keybase1.TeamRole_OWNER, invite.Role) 194 require.Len(t, team.chain().ActiveInvites(), 1, "number of invites") 195 } 196 197 // Test loading an implicit team as a #reader. 198 func TestImplicitTeamReader(t *testing.T) { 199 fus, tcs, cleanup := setupNTests(t, 2) 200 defer cleanup() 201 202 displayName := "" + fus[0].Username + ",bob@twitter#" + fus[1].Username 203 t.Logf("U0 creates an implicit team: %v", displayName) 204 team, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, displayName, false /*public*/) 205 require.NoError(t, err) 206 207 t.Logf("U1 looks up the team") 208 team2, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, displayName, false /*public*/) 209 require.NoError(t, err) 210 require.Equal(t, team.ID, team2.ID, "users should lookup the same team ID") 211 212 t.Logf("U1 loads the team") 213 team, err = Load(context.Background(), tcs[1].G, keybase1.LoadTeamArg{ID: team2.ID}) 214 require.NoError(t, err) 215 _, err = team.ApplicationKey(context.Background(), keybase1.TeamApplication_KBFS) 216 require.NoError(t, err, "getting kbfs application key") 217 require.False(t, team.IsPublic()) 218 u0Role, err := team.chain().GetUserRole(fus[0].GetUserVersion()) 219 require.NoError(t, err) 220 require.Equal(t, keybase1.TeamRole_OWNER, u0Role) 221 u1Role, err := team.chain().GetUserRole(fus[1].GetUserVersion()) 222 require.NoError(t, err) 223 require.Equal(t, keybase1.TeamRole_READER, u1Role) 224 } 225 226 // Check that ParseImplicitTeamDisplayName and FormatImplicitTeamDisplayName agree. 227 func TestImplicitDisplayTeamNameParse(t *testing.T) { 228 tc := SetupTest(t, "team", 1) 229 defer tc.Cleanup() 230 231 // TODO test this with keybase assertions (puk-less users). 232 // It will probably fail because <uid>@keybase is the wrong format. 233 234 makeAssertionContext := func() libkb.AssertionContext { 235 return libkb.MakeAssertionContext(libkb.NewMetaContext(context.Background(), tc.G), externals.NewProofServices(tc.G)) 236 } 237 238 for _, public := range []bool{true, false} { 239 for _, hasConflict := range []bool{true, false} { 240 var conflictInfo *keybase1.ImplicitTeamConflictInfo 241 if hasConflict { 242 conflictTime, err := time.Parse("2006-01-02", "2017-08-30") 243 require.NoError(t, err) 244 conflictInfo = &keybase1.ImplicitTeamConflictInfo{ 245 Generation: 3, 246 Time: keybase1.ToTime(conflictTime.UTC()), 247 } 248 } 249 obj1 := keybase1.ImplicitTeamDisplayName{ 250 IsPublic: public, 251 Writers: keybase1.ImplicitTeamUserSet{ 252 KeybaseUsers: []string{"alice", "bob"}, 253 UnresolvedUsers: []keybase1.SocialAssertion{ 254 {User: "twwwww", Service: keybase1.SocialAssertionService("twitter")}, 255 {User: "reeeee", Service: keybase1.SocialAssertionService("reddit")}, 256 }, 257 }, 258 Readers: keybase1.ImplicitTeamUserSet{ 259 KeybaseUsers: []string{"trust", "worthy"}, 260 UnresolvedUsers: []keybase1.SocialAssertion{ 261 {User: "ghhhh", Service: keybase1.SocialAssertionService("github")}, 262 {User: "fbbbb", Service: keybase1.SocialAssertionService("facebook")}, 263 }, 264 }, 265 ConflictInfo: conflictInfo, 266 } 267 str1, err := FormatImplicitTeamDisplayName(context.Background(), tc.G, obj1) 268 t.Logf("str1 '%v'", str1) 269 require.NoError(t, err) 270 obj2, err := libkb.ParseImplicitTeamDisplayName(makeAssertionContext(), str1, obj1.IsPublic) 271 require.NoError(t, err) 272 require.Equal(t, obj2.IsPublic, public) 273 require.Len(t, obj2.Writers.KeybaseUsers, 2) 274 require.Len(t, obj2.Writers.UnresolvedUsers, 2) 275 require.Len(t, obj2.Readers.KeybaseUsers, 2) 276 require.Len(t, obj2.Readers.UnresolvedUsers, 2) 277 if hasConflict { 278 require.NotNil(t, obj2.ConflictInfo) 279 require.Equal(t, obj2.ConflictInfo.Generation, obj1.ConflictInfo.Generation) 280 require.Equal(t, obj2.ConflictInfo.Time, obj1.ConflictInfo.Time) 281 } else { 282 require.Nil(t, obj2.ConflictInfo) 283 } 284 str2, err := FormatImplicitTeamDisplayName(context.Background(), tc.G, obj2) 285 require.NoError(t, err) 286 require.Equal(t, str2, str1) 287 } 288 } 289 } 290 291 // Test the looking up an implicit team involving a resolved assertion gives the resolved iteam. 292 func TestLookupImplicitTeamResolvedSocialAssertion(t *testing.T) { 293 fus, tcs, cleanup := setupNTests(t, 1) 294 defer cleanup() 295 296 // assumption: t_tracy@rooter resolves to t_tracy 297 298 displayName1 := "t_tracy@rooter," + fus[0].Username 299 displayName2 := "t_tracy," + fus[0].Username 300 301 team1, _, impTeamName1, err := LookupOrCreateImplicitTeam(context.TODO(), tcs[0].G, displayName1, false /*isPublic*/) 302 require.NoError(t, err) 303 team2, _, _, err := LookupOrCreateImplicitTeam(context.TODO(), tcs[0].G, displayName2, false /*isPublic*/) 304 require.NoError(t, err) 305 306 require.Equal(t, team1.ID, team2.ID, "implicit team ID should be the same for %v and %v", displayName1, displayName2) 307 308 team, err := Load(context.TODO(), tcs[0].G, keybase1.LoadTeamArg{ 309 ID: team1.ID, 310 }) 311 require.NoError(t, err) 312 owners, err := team.UsersWithRole(keybase1.TeamRole_OWNER) 313 require.NoError(t, err) 314 // Note: t_tracy has no PUK so she shows up as an invite. 315 require.Len(t, owners, 1) 316 require.Len(t, team.chain().ActiveInvites(), 1, "number of invites") 317 318 teamDisplay, err := team.ImplicitTeamDisplayNameString(context.TODO()) 319 require.NoError(t, err) 320 require.Equal(t, displayName2, teamDisplay) 321 formatName, err := FormatImplicitTeamDisplayName(context.TODO(), tcs[0].G, impTeamName1) 322 require.NoError(t, err) 323 require.Equal(t, displayName2, formatName) 324 } 325 326 // Test that you can rotate the key on an implicit team. 327 func TestImplicitTeamRotate(t *testing.T) { 328 for _, public := range []bool{false, true} { 329 t.Logf("public:%v", public) 330 fus, tcs, cleanup := setupNTests(t, 3) 331 defer cleanup() 332 333 displayName := strings.Join([]string{fus[0].Username, fus[1].Username}, ",") 334 335 team, _, _, err := LookupOrCreateImplicitTeam(context.TODO(), tcs[0].G, displayName, public) 336 require.NoError(t, err) 337 teamID := team.ID 338 t.Logf("teamID: %v", teamID) 339 require.Equal(t, keybase1.PerTeamKeyGeneration(1), team.Generation()) 340 341 t.Logf("rotate the key") 342 err = team.Rotate(context.TODO(), keybase1.RotationType_VISIBLE) 343 require.NoError(t, err) 344 345 t.Logf("load as other member") 346 team, err = Load(context.TODO(), tcs[1].G, keybase1.LoadTeamArg{ 347 ID: teamID, 348 Public: public, 349 }) 350 require.NoError(t, err) 351 require.Equal(t, keybase1.PerTeamKeyGeneration(2), team.Generation()) 352 353 if public { 354 t.Logf("load as third user who is not a member of the team") 355 team, err = Load(context.TODO(), tcs[1].G, keybase1.LoadTeamArg{ 356 ID: teamID, 357 Public: public, 358 }) 359 require.NoError(t, err) 360 require.Equal(t, keybase1.PerTeamKeyGeneration(2), team.Generation()) 361 } 362 } 363 } 364 365 func TestLoggedOutPublicTeamLoad(t *testing.T) { 366 tc := SetupTest(t, "team", 1) 367 defer tc.Cleanup() 368 u, err := kbtest.CreateAndSignupFakeUser("t", tc.G) 369 require.NoError(t, err) 370 createdTeam, _, impTeamName, err := LookupOrCreateImplicitTeam(context.TODO(), tc.G, u.Username, true) 371 require.NoError(t, err) 372 require.Equal(t, true, impTeamName.IsPublic) 373 err = tc.Logout() 374 require.NoError(t, err) 375 376 for i := 0; i < 2; i++ { 377 _, err = Load(context.TODO(), tc.G, keybase1.LoadTeamArg{ 378 ID: createdTeam.ID, 379 Public: true, 380 }) 381 require.NoError(t, err) 382 } 383 } 384 385 func TestImplicitInvalidLinks(t *testing.T) { 386 fus, tcs, cleanup := setupNTestsWithPukless(t, 5, 2) 387 defer cleanup() 388 389 ann := fus[0] // pukful user 390 bob := fus[1] // pukful user 391 cat := fus[3] // pukless user 392 393 pam := fus[2] // pukful user 394 joe := fus[4] // pukless user 395 396 impteamName := strings.Join([]string{ann.Username, bob.Username, cat.Username}, ",") 397 t.Logf("ann creates an implicit team: %v", impteamName) 398 teamObj, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, impteamName, false /*isPublic*/) 399 require.NoError(t, err) 400 401 { 402 // Adding entirely new member should be illegal 403 req := keybase1.TeamChangeReq{ 404 Owners: []keybase1.UserVersion{pam.GetUserVersion()}, 405 } 406 err := teamObj.ChangeMembership(context.Background(), req) 407 requirePrecheckError(t, err) 408 } 409 410 { 411 // Adding entirely new pukless member should be illegal 412 invite := SCTeamInvite{ 413 Type: "keybase", 414 Name: joe.GetUserVersion().TeamInviteName(), 415 ID: NewInviteID(), 416 } 417 err := teamObj.postInvite(context.Background(), invite, keybase1.TeamRole_OWNER) 418 requirePrecheckError(t, err) 419 } 420 421 { 422 // Adding new social invite never works 423 _, err := teamObj.inviteSBSMember(context.Background(), ann.Username+"@rooter", keybase1.TeamRole_OWNER) 424 requirePrecheckError(t, err) 425 } 426 427 { 428 // Removing existing member should be illegal 429 req := keybase1.TeamChangeReq{ 430 None: []keybase1.UserVersion{bob.GetUserVersion()}, 431 } 432 err := teamObj.ChangeMembership(context.Background(), req) 433 requirePrecheckError(t, err) 434 } 435 436 { 437 // Removing existing pukless member should be illegal 438 invite, _, found := teamObj.FindActiveKeybaseInvite(cat.GetUID()) 439 require.True(t, found) 440 err := removeInviteID(context.Background(), teamObj, invite.Id) 441 requirePrecheckError(t, err) 442 } 443 } 444 445 func TestImpTeamAddInviteWithoutCanceling(t *testing.T) { 446 fus, tcs, cleanup := setupNTestsWithPukless(t, 2, 1) 447 defer cleanup() 448 449 impteamName := strings.Join([]string{fus[0].Username, fus[1].Username}, ",") 450 t.Logf("created implicit team: %s", impteamName) 451 452 teamObj, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, impteamName, false /*isPublic*/) 453 require.NoError(t, err) 454 455 t.Logf("created team id: %s", teamObj.ID) 456 457 kbtest.ResetAccount(*tcs[1], fus[1]) 458 fus[1].EldestSeqno = 0 459 460 // Adding new version of user without canceling old invite should 461 // fail on the server side. 462 invite := SCTeamInvite{ 463 Type: "keybase", 464 Name: fus[1].GetUserVersion().TeamInviteName(), 465 ID: NewInviteID(), 466 } 467 err = teamObj.postInvite(context.Background(), invite, keybase1.TeamRole_OWNER) 468 require.IsType(t, libkb.AppStatusError{}, err) 469 } 470 471 func TestTeamListImplicit(t *testing.T) { 472 fus, tcs, cleanup := setupNTests(t, 2) 473 defer cleanup() 474 475 impteamName := strings.Join([]string{fus[0].Username, fus[1].Username}, ",") 476 t.Logf("created implicit team: %s", impteamName) 477 _, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, impteamName, false /*isPublic*/) 478 require.NoError(t, err) 479 480 teamName := createTeam(*tcs[1]) 481 t.Logf("created normal team: %s", teamName) 482 483 require.NoError(t, SetRoleWriter(context.Background(), tcs[1].G, teamName, fus[0].Username)) 484 485 list, err := ListTeamsVerified(context.Background(), tcs[0].G, keybase1.TeamListVerifiedArg{IncludeImplicitTeams: false}) 486 require.NoError(t, err) 487 require.Len(t, list.Teams, 1) 488 489 list, err = ListTeamsVerified(context.Background(), tcs[0].G, keybase1.TeamListVerifiedArg{IncludeImplicitTeams: true}) 490 require.NoError(t, err) 491 require.Len(t, list.Teams, 2) 492 493 list, err = ListTeamsUnverified(context.Background(), tcs[0].G, keybase1.TeamListUnverifiedArg{IncludeImplicitTeams: false}) 494 require.NoError(t, err) 495 require.Len(t, list.Teams, 1) 496 // verify that we cache this call 497 var cachedList []keybase1.MemberInfo 498 cacheKey := listTeamsUnverifiedCacheKey(fus[0].User.GetUID(), "" /* userAssertion */, false /* includeImplicitTeams */) 499 found, err := tcs[0].G.GetKVStore().GetInto(&cachedList, cacheKey) 500 require.NoError(t, err) 501 require.True(t, found) 502 require.Equal(t, len(list.Teams), len(cachedList)) 503 504 list, err = ListTeamsUnverified(context.Background(), tcs[0].G, keybase1.TeamListUnverifiedArg{IncludeImplicitTeams: true}) 505 require.NoError(t, err) 506 require.Len(t, list.Teams, 2) 507 cacheKey = listTeamsUnverifiedCacheKey(fus[0].User.GetUID(), "" /* userAssertion */, true /* includeImplicitTeams */) 508 found, err = tcs[0].G.GetKVStore().GetInto(&cachedList, cacheKey) 509 require.NoError(t, err) 510 require.True(t, found) 511 require.Equal(t, len(list.Teams), len(cachedList)) 512 513 list, err = ListAll(context.Background(), tcs[0].G, keybase1.TeamListTeammatesArg{ 514 IncludeImplicitTeams: false, 515 }) 516 require.NoError(t, err) 517 require.Len(t, list.Teams, 2) 518 require.Equal(t, teamName, list.Teams[0].FqName) 519 require.Equal(t, teamName, list.Teams[1].FqName) 520 521 list, err = ListAll(context.Background(), tcs[0].G, keybase1.TeamListTeammatesArg{ 522 IncludeImplicitTeams: true, 523 }) 524 require.NoError(t, err) 525 require.Len(t, list.Teams, 4) 526 } 527 528 func TestReAddMemberWithSameUV(t *testing.T) { 529 fus, tcs, cleanup := setupNTestsWithPukless(t, 4, 2) 530 defer cleanup() 531 532 ann := fus[0] // crypto user 533 bob := fus[1] // crypto user 534 jun := fus[2] // pukless user 535 hal := fus[3] // pukless user (eldest=0) 536 537 tcAnn := tcs[0] // crypto user 538 tcBob := tcs[1] // crypto user 539 tcHal := tcs[3] // pukless user (eldest=0) 540 541 kbtest.ResetAccount(*tcHal, hal) // reset hal 542 543 impteamName := strings.Join([]string{ann.Username, bob.Username, jun.Username, hal.Username}, ",") 544 t.Logf("ann creates an implicit team: %v", impteamName) 545 teamObj, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcAnn.G, impteamName, false /*isPublic*/) 546 require.NoError(t, err) 547 548 t.Logf("created team id: %s", teamObj.ID) 549 550 err = reAddMemberAfterResetInner(context.Background(), tcAnn.G, teamObj.ID, bob.Username) 551 require.IsType(t, UserHasNotResetError{}, err) 552 553 err = ReAddMemberAfterReset(context.Background(), tcAnn.G, teamObj.ID, jun.Username) 554 require.NoError(t, err) // error should be suppressed 555 556 err = reAddMemberAfterResetInner(context.Background(), tcAnn.G, teamObj.ID, hal.Username) 557 require.IsType(t, UserHasNotResetError{}, err) 558 559 // Now, the fun part (bug CORE-8099): 560 561 // Bob resets, ann re-adds bob by posting an "invite" link, so 562 // from chain point of view there are two active memberships for 563 // bob: cryptomember from before reset and invite from after reset 564 // (it's an implicit team weirdness - "invite" link has no way of 565 // removing old membership). 566 567 kbtest.ResetAccount(*tcBob, bob) // reset bob 568 err = reAddMemberAfterResetInner(context.Background(), tcAnn.G, teamObj.ID, bob.Username) 569 require.NoError(t, err) 570 571 // Subsequent calls should start UserHasNotResetErrorin again 572 err = reAddMemberAfterResetInner(context.Background(), tcAnn.G, teamObj.ID, bob.Username) 573 require.IsType(t, UserHasNotResetError{}, err) 574 } 575 576 func TestBotMember(t *testing.T) { 577 fus, tcs, cleanup := setupNTests(t, 4) 578 defer cleanup() 579 580 ann := fus[0] // crypto user 581 bob := fus[1] // crypto user 582 botua := fus[2] // bot user 583 restrictedBotua := fus[3] // restricted bot user 584 585 impteamName := strings.Join([]string{ann.Username, bob.Username}, ",") 586 t.Logf("ann creates an implicit team: %v", impteamName) 587 teamObj, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, impteamName, false /*isPublic*/) 588 require.NoError(t, err) 589 590 t.Logf("created team id: %s", teamObj.ID) 591 _, err = AddMemberByID(context.TODO(), tcs[0].G, teamObj.ID, botua.Username, keybase1.TeamRole_BOT, nil, nil /* emailInviteMsg */) 592 require.NoError(t, err) 593 _, err = AddMemberByID(context.TODO(), tcs[0].G, teamObj.ID, restrictedBotua.Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{}, nil /* emailInviteMsg */) 594 require.NoError(t, err) 595 team, err := Load(context.Background(), tcs[2].G, keybase1.LoadTeamArg{ID: teamObj.ID}) 596 require.NoError(t, err) 597 598 members, err := team.Members() 599 require.NoError(t, err) 600 require.Len(t, members.Bots, 1) 601 require.Equal(t, botua.User.GetUID(), members.Bots[0].Uid) 602 require.Len(t, members.RestrictedBots, 1) 603 require.Equal(t, restrictedBotua.User.GetUID(), members.RestrictedBots[0].Uid) 604 605 team, err = Load(context.Background(), tcs[3].G, keybase1.LoadTeamArg{ID: teamObj.ID}) 606 require.NoError(t, err) 607 608 members, err = team.Members() 609 require.NoError(t, err) 610 require.Len(t, members.Bots, 1) 611 require.Equal(t, botua.User.GetUID(), members.Bots[0].Uid) 612 require.Len(t, members.RestrictedBots, 1) 613 require.Equal(t, restrictedBotua.User.GetUID(), members.RestrictedBots[0].Uid) 614 615 kbtest.ResetAccount(*tcs[2], botua) 616 err = ReAddMemberAfterReset(context.Background(), tcs[0].G, teamObj.ID, botua.Username) 617 require.Error(t, err) 618 619 err = botua.Login(tcs[2].G) 620 require.NoError(t, err) 621 err = kbtest.AssertProvisioned(*tcs[2]) 622 require.NoError(t, err) 623 624 err = ReAddMemberAfterReset(context.Background(), tcs[0].G, teamObj.ID, botua.Username) 625 require.NoError(t, err) 626 // Subsequent calls should have UserHasNotResetError 627 err = reAddMemberAfterResetInner(context.Background(), tcs[0].G, teamObj.ID, botua.Username) 628 require.IsType(t, UserHasNotResetError{}, err) 629 630 team, err = Load(context.Background(), tcs[3].G, keybase1.LoadTeamArg{ID: teamObj.ID}) 631 require.NoError(t, err) 632 633 members, err = team.Members() 634 require.NoError(t, err) 635 636 kbtest.ResetAccount(*tcs[3], restrictedBotua) 637 team, err = Load(context.Background(), tcs[2].G, keybase1.LoadTeamArg{ID: teamObj.ID}) 638 require.NoError(t, err) 639 640 members, err = team.Members() 641 require.NoError(t, err) 642 // RESTRICTEDBOT invites not supported 643 err = ReAddMemberAfterReset(context.Background(), tcs[0].G, teamObj.ID, restrictedBotua.Username) 644 require.Error(t, err) 645 646 err = restrictedBotua.Login(tcs[3].G) 647 require.NoError(t, err) 648 err = kbtest.AssertProvisioned(*tcs[3]) 649 require.NoError(t, err) 650 651 err = ReAddMemberAfterReset(context.Background(), tcs[0].G, teamObj.ID, restrictedBotua.Username) 652 require.NoError(t, err) 653 654 // Subsequent calls should have UserHasNotResetError 655 err = reAddMemberAfterResetInner(context.Background(), tcs[0].G, teamObj.ID, restrictedBotua.Username) 656 require.IsType(t, UserHasNotResetError{}, err) 657 658 team, err = Load(context.Background(), tcs[0].G, keybase1.LoadTeamArg{ID: teamObj.ID}) 659 require.NoError(t, err) 660 members, err = team.Members() 661 require.NoError(t, err) 662 require.Len(t, members.Bots, 1) 663 require.Equal(t, botua.User.GetUID(), members.Bots[0].Uid) 664 require.Len(t, members.RestrictedBots, 1) 665 require.Equal(t, restrictedBotua.User.GetUID(), members.RestrictedBots[0].Uid) 666 667 // we can change bot memberships, but cannot have a non-bot like role 668 err = EditMemberByID(context.TODO(), tcs[0].G, teamObj.ID, botua.Username, keybase1.TeamRole_RESTRICTEDBOT, &keybase1.TeamBotSettings{}) 669 require.NoError(t, err) 670 671 err = EditMemberByID(context.TODO(), tcs[0].G, teamObj.ID, restrictedBotua.Username, keybase1.TeamRole_WRITER, nil) 672 require.Error(t, err) 673 674 err = EditMemberByID(context.TODO(), tcs[0].G, teamObj.ID, restrictedBotua.Username, keybase1.TeamRole_BOT, nil) 675 require.NoError(t, err) 676 677 team, err = Load(context.Background(), tcs[0].G, keybase1.LoadTeamArg{ID: teamObj.ID}) 678 require.NoError(t, err) 679 members, err = team.Members() 680 require.NoError(t, err) 681 require.Len(t, members.Bots, 1) 682 require.Equal(t, restrictedBotua.User.GetUID(), members.Bots[0].Uid) 683 require.Len(t, members.RestrictedBots, 1) 684 require.Equal(t, botua.User.GetUID(), members.RestrictedBots[0].Uid) 685 686 err = RemoveMemberByID(context.TODO(), tcs[0].G, teamObj.ID, botua.Username) 687 require.NoError(t, err) 688 err = RemoveMemberByID(context.TODO(), tcs[0].G, teamObj.ID, restrictedBotua.Username) 689 require.NoError(t, err) 690 691 team, err = Load(context.Background(), tcs[0].G, keybase1.LoadTeamArg{ID: teamObj.ID}) 692 require.NoError(t, err) 693 members, err = team.Members() 694 require.NoError(t, err) 695 require.Len(t, members.Bots, 0) 696 require.Len(t, members.RestrictedBots, 0) 697 } 698 699 func TestGetTeamIDRPC(t *testing.T) { 700 fus, tcs, cleanup := setupNTests(t, 2) 701 defer cleanup() 702 703 for i := 1; i <= 2; i++ { 704 // Test with two impteams: "fus[0]" and "fus[0],fus[1]" 705 var membersStr []string 706 for j := 0; j < i; j++ { 707 membersStr = append(membersStr, fus[j].Username) 708 } 709 impteamName := strings.Join(membersStr, ",") 710 t.Logf("creating an implicit team: %v", impteamName) 711 teamObj, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, impteamName, false /*isPublic*/) 712 require.NoError(t, err) 713 714 mctx := libkb.NewMetaContextForTest(*tcs[0]) 715 res, err := GetTeamIDByNameRPC(mctx, teamObj.Name().String()) 716 require.NoError(t, err) 717 require.Equal(t, teamObj.ID, res) 718 } 719 } 720 721 func TestInvalidPhoneNumberAssertion(t *testing.T) { 722 fus, tcs, cleanup := setupNTests(t, 1) 723 defer cleanup() 724 725 // Make sure we are stopped from creating an implicit team with bad number. 726 // This will also stop a conversation from being created if someone tries 727 // to chat with invalid phone number assertion. 728 badNumbers := []string{"111", "12345678", "48111"} 729 for _, bad := range badNumbers { 730 displayName := keybase1.ImplicitTeamDisplayName{ 731 IsPublic: false, 732 Writers: keybase1.ImplicitTeamUserSet{ 733 KeybaseUsers: []string{fus[0].Username}, 734 UnresolvedUsers: []keybase1.SocialAssertion{ 735 { 736 User: bad, 737 Service: keybase1.SocialAssertionService("phone"), 738 }, 739 }, 740 }, 741 } 742 t.Logf("Trying name: %q", displayName.String()) 743 _, _, err := CreateImplicitTeam(context.Background(), tcs[0].G, displayName) 744 require.Error(t, err) 745 require.Contains(t, err.Error(), "bad phone number given") 746 } 747 748 // Some numbers are stopped at assertion parsing level. 749 superBadNumbers := []string{"012345678"} 750 for _, bad := range superBadNumbers { 751 displayName := fmt.Sprintf("%s@phone,%s", bad, fus[0].Username) 752 _, err := ResolveImplicitTeamDisplayName(context.Background(), tcs[0].G, displayName, false) 753 require.Error(t, err) 754 require.Contains(t, err.Error(), "Invalid phone number") 755 } 756 } 757 758 func TestCaseSensitiveDisplayNames(t *testing.T) { 759 fus, tcs, cleanup := setupNTests(t, 1) 760 defer cleanup() 761 762 upEmail := strings.ToUpper(fus[0].Email) 763 displayNameInput := fmt.Sprintf("%s,[%s]@email", fus[0].Username, upEmail) 764 _, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, displayNameInput, false /*isPublic*/) 765 require.Error(t, err) 766 require.Contains(t, err.Error(), "Display name is not normalized") 767 require.Contains(t, err.Error(), upEmail) 768 769 rooter := fmt.Sprintf("%s@hackernews", strings.ToUpper(fus[0].Username)) 770 displayNameInput = fmt.Sprintf("%s,%s", fus[0].Username, rooter) 771 _, _, _, err = LookupOrCreateImplicitTeam(context.Background(), tcs[0].G, displayNameInput, false /*isPublic*/) 772 require.Error(t, err) 773 require.Contains(t, err.Error(), "Display name is not normalized") 774 require.Contains(t, err.Error(), rooter) 775 } 776 777 func TestReAddMemberAfterResetWithRestrictiveContactSettings(t *testing.T) { 778 fus, tcs, cleanup := setupNTestsWithPukless(t, 3, 1) 779 defer cleanup() 780 781 ann := fus[0] // crypto user 782 bob := fus[1] // crypto user 783 jun := fus[2] // pukless user 784 785 tcAnn := tcs[0] // crypto user 786 tcBob := tcs[1] // crypto user 787 tcJun := tcs[2] // pukless user 788 789 impteamName := strings.Join([]string{ann.Username, bob.Username, jun.Username}, ",") 790 t.Logf("ann creates an implicit team: %v", impteamName) 791 teamObj, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcAnn.G, impteamName, false /*isPublic*/) 792 require.NoError(t, err) 793 794 t.Logf("created team id: %s", teamObj.ID) 795 796 // bob resets 797 kbtest.ResetAccount(*tcBob, bob) 798 799 // bob sets contact settings 800 err = bob.Login(tcBob.G) 801 require.NoError(t, err) 802 kbtest.SetContactSettings(*tcBob, bob, keybase1.ContactSettings{ 803 Enabled: true, 804 AllowFolloweeDegrees: 0, 805 }) 806 err = tcBob.Logout() 807 require.NoError(t, err) 808 809 err = reAddMemberAfterResetInner(context.Background(), tcAnn.G, teamObj.ID, bob.Username) 810 require.NoError(t, err) // changing contact settings doesn't affect existing teams 811 812 // jun resets 813 kbtest.ResetAccount(*tcJun, jun) 814 815 // jun sets contact settings 816 err = jun.Login(tcJun.G) 817 require.NoError(t, err) 818 kbtest.SetContactSettings(*tcJun, jun, keybase1.ContactSettings{ 819 Enabled: true, 820 AllowFolloweeDegrees: 0, 821 }) 822 err = tcJun.Logout() 823 require.NoError(t, err) 824 825 err = reAddMemberAfterResetInner(context.Background(), tcAnn.G, teamObj.ID, jun.Username) 826 require.NoError(t, err) // changing contact settings doesn't affect existing teams 827 } 828 829 func TestLookupImplicitTeamWithRestrictiveContactSettings(t *testing.T) { 830 fus, tcs, cleanup := setupNTests(t, 3) 831 defer cleanup() 832 833 ann := fus[0] 834 bob := fus[1] 835 jun := fus[2] 836 837 tcAnn := tcs[0] 838 tcBob := tcs[1] 839 tcJun := tcs[2] 840 841 // jun sets contact settings 842 err := jun.Login(tcJun.G) 843 require.NoError(t, err) 844 kbtest.SetContactSettings(*tcJun, jun, keybase1.ContactSettings{ 845 Enabled: true, 846 AllowFolloweeDegrees: 1, 847 }) 848 849 // can't create implicit team with a user with restrictive contact settings 850 impteamName := strings.Join([]string{ann.Username, bob.Username, jun.Username}, ",") 851 _, _, _, err = LookupOrCreateImplicitTeam(context.Background(), tcAnn.G, impteamName, false /*isPublic*/) 852 require.Error(t, err) 853 require.IsType(t, err, libkb.TeamContactSettingsBlockError{}) 854 usernames := err.(libkb.TeamContactSettingsBlockError).BlockedUsernames() 855 require.Equal(t, 1, len(usernames)) 856 require.Equal(t, libkb.NewNormalizedUsername(jun.Username), usernames[0]) 857 858 // jun follows ann 859 _, err = kbtest.RunTrack(*tcJun, jun, ann.Username) 860 require.NoError(t, err) 861 err = tcJun.Logout() 862 require.NoError(t, err) 863 864 // try creating implicit team again; should succeed 865 teamObj, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcAnn.G, impteamName, false /*isPublic*/) 866 require.NoError(t, err) 867 868 t.Logf("created implicit team: %v; team id: %s", impteamName, teamObj.ID) 869 870 // bob sets contact settings 871 err = bob.Login(tcBob.G) 872 require.NoError(t, err) 873 kbtest.SetContactSettings(*tcBob, bob, keybase1.ContactSettings{ 874 Enabled: true, 875 AllowFolloweeDegrees: 0, 876 }) 877 err = tcBob.Logout() 878 require.NoError(t, err) 879 880 // changing contact settings doesn't affect existing teams 881 teamObj2, _, _, err := LookupOrCreateImplicitTeam(context.Background(), tcAnn.G, impteamName, false /*isPublic*/) 882 require.NoError(t, err) 883 require.Equal(t, teamObj.ID, teamObj2.ID) 884 }