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  }