github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/service/teams.go (about)

     1  // Copyright 2017 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  // RPC handlers for team operations
     5  
     6  package service
     7  
     8  import (
     9  	"fmt"
    10  	"time"
    11  
    12  	"github.com/go-errors/errors"
    13  	"github.com/keybase/client/go/protocol/gregor1"
    14  
    15  	"github.com/keybase/client/go/kbtime"
    16  
    17  	"github.com/keybase/client/go/chat/globals"
    18  	"github.com/keybase/client/go/externals"
    19  	"github.com/keybase/client/go/libkb"
    20  	"github.com/keybase/client/go/offline"
    21  	"github.com/keybase/client/go/protocol/chat1"
    22  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    23  	"github.com/keybase/client/go/teams"
    24  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    25  	"golang.org/x/net/context"
    26  )
    27  
    28  type TeamsHandler struct {
    29  	*BaseHandler
    30  	globals.Contextified
    31  	connID  libkb.ConnectionID
    32  	service *Service
    33  }
    34  
    35  var _ keybase1.TeamsInterface = (*TeamsHandler)(nil)
    36  
    37  func NewTeamsHandler(xp rpc.Transporter, id libkb.ConnectionID, g *globals.Context, service *Service) *TeamsHandler {
    38  	return &TeamsHandler{
    39  		BaseHandler:  NewBaseHandler(g.ExternalG(), xp),
    40  		Contextified: globals.NewContextified(g),
    41  		connID:       id,
    42  		service:      service,
    43  	}
    44  }
    45  
    46  func (h *TeamsHandler) UntrustedTeamExists(ctx context.Context, teamName keybase1.TeamName) (res keybase1.UntrustedTeamExistsResult, err error) {
    47  	ctx = libkb.WithLogTag(ctx, "TM")
    48  	defer h.G().CTrace(ctx, fmt.Sprintf("UntrustedTeamExists(%s)", teamName), &err)()
    49  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
    50  	return teams.GetUntrustedTeamExists(mctx, teamName)
    51  }
    52  
    53  func (h *TeamsHandler) TeamCreate(ctx context.Context, arg keybase1.TeamCreateArg) (res keybase1.TeamCreateResult, err error) {
    54  	ctx = libkb.WithLogTag(ctx, "TM")
    55  	arg2 := keybase1.TeamCreateWithSettingsArg{
    56  		SessionID:   arg.SessionID,
    57  		Name:        arg.Name,
    58  		JoinSubteam: arg.JoinSubteam,
    59  	}
    60  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
    61  		return res, err
    62  	}
    63  	return h.TeamCreateWithSettings(ctx, arg2)
    64  }
    65  
    66  func (h *TeamsHandler) TeamCreateFancy(ctx context.Context, arg keybase1.TeamCreateFancyArg) (teamID keybase1.TeamID,
    67  	err error) {
    68  	ctx = libkb.WithLogTag(ctx, "TM")
    69  	mctx := h.MetaContext(ctx)
    70  	teamInfo := arg.TeamInfo
    71  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamCreateFancy(%s)", teamInfo.Name), &err)()
    72  
    73  	arg2 := keybase1.TeamCreateWithSettingsArg{
    74  		Name:        teamInfo.Name,
    75  		SessionID:   arg.SessionID,
    76  		Settings:    teamInfo.OpenSettings,
    77  		JoinSubteam: teamInfo.JoinSubteam,
    78  	}
    79  	teamCreateRes, err := h.TeamCreateWithSettings(ctx, arg2)
    80  	if err != nil {
    81  		return teamID, err
    82  	}
    83  	teamID = teamCreateRes.TeamID
    84  
    85  	var errs []error
    86  	if teamInfo.Description != "" {
    87  		err = teams.SetTeamShowcase(ctx, h.G().ExternalG(), teamID, nil, /* showcase */
    88  			&teamInfo.Description, nil /* anyMemberShowcase */)
    89  		if err != nil {
    90  			errs = append(errs, err)
    91  			mctx.Error("SetTeamShowcase failed with: %s", err)
    92  		}
    93  	}
    94  	if teamInfo.ProfileShowcase {
    95  		err = teams.SetTeamMemberShowcase(ctx, h.G().ExternalG(), teamID, true /* isShowcased */)
    96  		if err != nil {
    97  			errs = append(errs, err)
    98  			mctx.Error("SetTeamMemberShowcase failed with: %s", err)
    99  		}
   100  	}
   101  	if teamInfo.Avatar != nil {
   102  		avatar := teamInfo.Avatar
   103  		arg3 := keybase1.UploadTeamAvatarArg{Teamname: teamInfo.Name, Filename: avatar.AvatarFilename,
   104  			Crop: avatar.Crop, SendChatNotification: false}
   105  		err = teams.ChangeTeamAvatar(mctx, arg3)
   106  		if err != nil {
   107  			errs = append(errs, err)
   108  		}
   109  	}
   110  	uid := gregor1.UID(h.G().GetMyUID().ToBytes())
   111  	for _, topicName := range teamInfo.ChatChannels {
   112  		_, _, err = h.G().ChatHelper.NewConversation(ctx, uid, teamInfo.Name,
   113  			&topicName, chat1.TopicType_CHAT, chat1.ConversationMembersType_TEAM, keybase1.TLFVisibility_PRIVATE)
   114  		if err != nil {
   115  			errs = append(errs, err)
   116  		}
   117  	}
   118  	for _, subteamName := range teamInfo.Subteams {
   119  		name := teamInfo.Name + "." + subteamName
   120  		_, err = h.TeamCreate(ctx, keybase1.TeamCreateArg{SessionID: arg.SessionID, Name: name})
   121  		if err != nil {
   122  			errs = append(errs, err)
   123  		}
   124  	}
   125  
   126  	if len(teamInfo.Users) > 0 {
   127  		arg4 := keybase1.TeamAddMembersMultiRoleArg{
   128  			SessionID:            arg.SessionID,
   129  			TeamID:               teamID,
   130  			Users:                teamInfo.Users,
   131  			SendChatNotification: false,
   132  			EmailInviteMessage:   teamInfo.EmailInviteMessage,
   133  			// Add users to the default channels.
   134  			AddToChannels: nil,
   135  		}
   136  
   137  		unaddedUsers, err := h.TeamAddMembersMultiRole(ctx, arg4)
   138  		if err != nil {
   139  			errs = append(errs, err)
   140  		}
   141  		if len(unaddedUsers.NotAdded) > 0 {
   142  			errs = append(errs, fmt.Errorf("could not add members to team: %v", unaddedUsers.NotAdded))
   143  		}
   144  	}
   145  
   146  	if errs == nil {
   147  		return teamID, nil
   148  	}
   149  	return teamID, libkb.CombineErrors(errs...)
   150  }
   151  
   152  func (h *TeamsHandler) TeamCreateWithSettings(ctx context.Context, arg keybase1.TeamCreateWithSettingsArg) (res keybase1.TeamCreateResult, err error) {
   153  	ctx = libkb.WithLogTag(ctx, "TM")
   154  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamCreate(%s)", arg.Name), &err)()
   155  
   156  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   157  		return res, err
   158  	}
   159  
   160  	teamName, err := keybase1.TeamNameFromString(arg.Name)
   161  	if err != nil {
   162  		return res, err
   163  	}
   164  	if teamName.Depth() == 0 {
   165  		return res, fmt.Errorf("empty team name")
   166  	}
   167  	if !teamName.IsRootTeam() {
   168  		h.G().Log.CDebugf(ctx, "TeamCreate: creating a new subteam: %s", arg.Name)
   169  		parentName, err := teamName.Parent()
   170  		if err != nil {
   171  			return res, err
   172  		}
   173  		addSelfAs := keybase1.TeamRole_WRITER // writer is enough to init chat channel
   174  		if arg.JoinSubteam {
   175  			// but if user wants to stay in team, add as admin.
   176  			addSelfAs = keybase1.TeamRole_ADMIN
   177  		}
   178  
   179  		teamID, err := teams.CreateSubteam(ctx, h.G().ExternalG(), string(teamName.LastPart()),
   180  			parentName, addSelfAs)
   181  		if err != nil {
   182  			return res, err
   183  		}
   184  		res.TeamID = *teamID
   185  
   186  		// join the team to send the Create message
   187  		h.G().Log.CDebugf(ctx, "TeamCreate: created subteam %s with self in as %v", arg.Name, addSelfAs)
   188  		username := h.G().Env.GetUsername().String()
   189  		res.ChatSent = teams.SendTeamChatCreateMessage(ctx, h.G().ExternalG(), teamName.String(), username)
   190  		res.CreatorAdded = true
   191  
   192  		if !arg.JoinSubteam {
   193  			h.G().Log.CDebugf(ctx, "TeamCreate: leaving just-created subteam %s", arg.Name)
   194  			if err := teams.Leave(ctx, h.G().ExternalG(), teamName.String(), false); err != nil {
   195  				h.G().Log.CDebugf(ctx, "TeamCreate: error leaving new subteam %s: %s", arg.Name, err)
   196  				return res, err
   197  			}
   198  			h.G().Log.CDebugf(ctx, "TeamCreate: left just-created subteam %s", arg.Name)
   199  			res.CreatorAdded = false
   200  		}
   201  	} else {
   202  		teamID, err := teams.CreateRootTeam(ctx, h.G().ExternalG(), teamName.String(), arg.Settings)
   203  		if err != nil {
   204  			return res, err
   205  		}
   206  		res.TeamID = *teamID
   207  		res.CreatorAdded = true
   208  		// send system message that team was created
   209  		res.ChatSent = teams.SendTeamChatCreateMessage(ctx, h.G().ExternalG(), teamName.String(), h.G().Env.GetUsername().String())
   210  	}
   211  
   212  	return res, nil
   213  }
   214  
   215  func (h *TeamsHandler) TeamGet(ctx context.Context, arg keybase1.TeamGetArg) (res keybase1.TeamDetails, err error) {
   216  	ctx = libkb.WithLogTag(ctx, "TM")
   217  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamGet(%s)", arg.Name), &err)()
   218  
   219  	t, err := teams.GetAnnotatedTeamByName(ctx, h.G().ExternalG(), arg.Name)
   220  	if err != nil {
   221  		return res, err
   222  	}
   223  	return t.ToLegacyTeamDetails(), nil
   224  }
   225  
   226  func (h *TeamsHandler) TeamGetByID(ctx context.Context, arg keybase1.TeamGetByIDArg) (res keybase1.TeamDetails, err error) {
   227  	ctx = libkb.WithLogTag(ctx, "TM")
   228  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamGetByID(%s)", arg.Id), &err)()
   229  
   230  	t, err := teams.GetAnnotatedTeam(ctx, h.G().ExternalG(), arg.Id)
   231  	if err != nil {
   232  		return res, err
   233  	}
   234  	return t.ToLegacyTeamDetails(), nil
   235  }
   236  
   237  func (h *TeamsHandler) GetAnnotatedTeam(ctx context.Context, arg keybase1.TeamID) (res keybase1.AnnotatedTeam, err error) {
   238  	ctx = libkb.WithLogTag(ctx, "TM")
   239  	defer h.G().CTrace(ctx, fmt.Sprintf("GetAnnotatedTeam(%s)", arg), &err)()
   240  
   241  	return teams.GetAnnotatedTeam(ctx, h.G().ExternalG(), arg)
   242  }
   243  
   244  func (h *TeamsHandler) GetAnnotatedTeamByName(ctx context.Context, arg string) (res keybase1.AnnotatedTeam, err error) {
   245  	ctx = libkb.WithLogTag(ctx, "TM")
   246  	defer h.G().CTrace(ctx, fmt.Sprintf("GetAnnotatedTeam(%s)", arg), &err)()
   247  
   248  	return teams.GetAnnotatedTeamByName(ctx, h.G().ExternalG(), arg)
   249  }
   250  
   251  func (h *TeamsHandler) TeamGetMembersByID(ctx context.Context, arg keybase1.TeamGetMembersByIDArg) (res []keybase1.TeamMemberDetails, err error) {
   252  	ctx = libkb.WithLogTag(ctx, "TM")
   253  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamGetMembersByID(%s)", arg.Id), &err)()
   254  
   255  	t, err := teams.GetAnnotatedTeam(ctx, h.G().ExternalG(), arg.Id)
   256  	if err != nil {
   257  		return res, err
   258  	}
   259  	return t.Members, nil
   260  }
   261  
   262  func (h *TeamsHandler) TeamListUnverified(ctx context.Context, arg keybase1.TeamListUnverifiedArg) (res keybase1.AnnotatedTeamList, err error) {
   263  	ctx = libkb.WithLogTag(ctx, "TM")
   264  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamList(%s)", arg.UserAssertion), &err)()
   265  	x, err := teams.ListTeamsUnverified(ctx, h.G().ExternalG(), arg)
   266  	if err != nil {
   267  		return keybase1.AnnotatedTeamList{}, err
   268  	}
   269  	return *x, nil
   270  }
   271  
   272  func (h *TeamsHandler) TeamListTeammates(ctx context.Context, arg keybase1.TeamListTeammatesArg) (res keybase1.AnnotatedTeamList, err error) {
   273  	ctx = libkb.WithLogTag(ctx, "TM")
   274  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamListTeammates(%t)", arg.IncludeImplicitTeams), &err)()
   275  	x, err := teams.ListAll(ctx, h.G().ExternalG(), arg)
   276  	if err != nil {
   277  		return keybase1.AnnotatedTeamList{}, err
   278  	}
   279  	return *x, nil
   280  }
   281  
   282  func (h *TeamsHandler) TeamListVerified(ctx context.Context, arg keybase1.TeamListVerifiedArg) (res keybase1.AnnotatedTeamList, err error) {
   283  	ctx = libkb.WithLogTag(ctx, "TM")
   284  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamListVerified(%s)", arg.UserAssertion), &err)()
   285  	x, err := teams.ListTeamsVerified(ctx, h.G().ExternalG(), arg)
   286  	if err != nil {
   287  		return keybase1.AnnotatedTeamList{}, err
   288  	}
   289  	return *x, nil
   290  }
   291  
   292  func (h *TeamsHandler) TeamGetSubteamsUnverified(ctx context.Context, arg keybase1.TeamGetSubteamsUnverifiedArg) (res keybase1.SubteamListResult, err error) {
   293  	ctx = libkb.WithLogTag(ctx, "TM")
   294  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamGetSubteamsUnverified(%s)", arg.Name), &err)()
   295  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   296  	return teams.ListSubteamsUnverified(mctx, arg.Name)
   297  }
   298  
   299  func (h *TeamsHandler) TeamListSubteamsRecursive(ctx context.Context, arg keybase1.TeamListSubteamsRecursiveArg) (res []keybase1.TeamIDAndName, err error) {
   300  	ctx = libkb.WithLogTag(ctx, "TM")
   301  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamListSubteamsRecursive(%s)", arg.ParentTeamName), &err)()
   302  	return teams.ListSubteamsRecursive(ctx, h.G().ExternalG(), arg.ParentTeamName, arg.ForceRepoll)
   303  }
   304  
   305  func (h *TeamsHandler) TeamAddMember(ctx context.Context, arg keybase1.TeamAddMemberArg) (res keybase1.TeamAddMemberResult, err error) {
   306  	ctx = libkb.WithLogTag(ctx, "TM")
   307  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamAddMember(%s,username=%q,email=%q,phone=%q)", arg.TeamID, arg.Username, arg.Email, arg.Phone),
   308  		&err)()
   309  
   310  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   311  		return res, err
   312  	}
   313  
   314  	assertion := arg.Username
   315  	if arg.Email != "" || arg.Phone != "" {
   316  		var assertionURL libkb.AssertionURL
   317  		var err error
   318  		if arg.Email != "" {
   319  			assertionURL, err = libkb.ParseAssertionURLKeyValue(externals.MakeStaticAssertionContext(ctx), "email", arg.Email, true)
   320  		} else {
   321  			assertionURL, err = libkb.ParseAssertionURLKeyValue(externals.MakeStaticAssertionContext(ctx), "phone", arg.Phone, true)
   322  		}
   323  		if err != nil {
   324  			return res, err
   325  		}
   326  		assertion = assertionURL.String()
   327  	}
   328  
   329  	result, err := teams.AddMemberByID(ctx, h.G().ExternalG(), arg.TeamID, assertion, arg.Role, arg.BotSettings, arg.EmailInviteMessage)
   330  	if err != nil {
   331  		return res, err
   332  	}
   333  
   334  	if !arg.SendChatNotification {
   335  		return result, nil
   336  	}
   337  
   338  	if result.Invited {
   339  		return result, nil
   340  	}
   341  
   342  	result.ChatSending = true
   343  	go func() {
   344  		ctx := libkb.WithLogTag(context.Background(), "BG")
   345  		err := teams.SendTeamChatWelcomeMessage(ctx, h.G().ExternalG(), arg.TeamID, "",
   346  			result.User.Username, chat1.ConversationMembersType_TEAM, arg.Role)
   347  		if err != nil {
   348  			h.G().Log.CDebugf(ctx, "send team welcome message: error: %v", err)
   349  		} else {
   350  			h.G().Log.CDebugf(ctx, "send team welcome message: success")
   351  		}
   352  	}()
   353  	return result, nil
   354  }
   355  
   356  // TeamAddMembers returns err if adding members to team failed.
   357  // Adding members is "all-or-nothing", except in the case that the 1st attempt
   358  // fails due to some users having restrictive contact settings. In this
   359  // situation, we retry adding just the non-restricted members. If this 2nd attempt
   360  // succeeds, err=nil and TeamAddMembersResult contains a list of the users that
   361  // weren't added. If the 2nd attempt fails, then an err is returned as usual.
   362  func (h *TeamsHandler) TeamAddMembers(ctx context.Context, arg keybase1.TeamAddMembersArg) (res keybase1.TeamAddMembersResult, err error) {
   363  	ctx = libkb.WithLogTag(ctx, "TM")
   364  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamAddMembers(%+v", arg), &err)()
   365  
   366  	var users []keybase1.UserRolePair
   367  	for _, a := range arg.Assertions {
   368  		users = append(users, keybase1.UserRolePair{Assertion: a, Role: arg.Role})
   369  	}
   370  	arg2 := keybase1.TeamAddMembersMultiRoleArg{
   371  		TeamID:               arg.TeamID,
   372  		Users:                users,
   373  		SendChatNotification: arg.SendChatNotification,
   374  		EmailInviteMessage:   arg.EmailInviteMessage,
   375  	}
   376  	return h.TeamAddMembersMultiRole(ctx, arg2)
   377  }
   378  
   379  func (h *TeamsHandler) TeamAddMembersMultiRole(ctx context.Context, arg keybase1.TeamAddMembersMultiRoleArg) (res keybase1.TeamAddMembersResult, err error) {
   380  	ctx = libkb.WithLogTag(ctx, "TM")
   381  
   382  	debugString := "0"
   383  	if len(arg.Users) > 0 {
   384  		debugString = fmt.Sprintf("'%v'", arg.Users[0].Assertion)
   385  		if len(arg.Users) > 1 {
   386  			debugString = fmt.Sprintf("'%v' + %v more", arg.Users[0].Assertion, len(arg.Users)-1)
   387  		}
   388  	}
   389  
   390  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamAddMembers(%s, %s)", arg.TeamID, debugString),
   391  		&err)()
   392  	if len(arg.Users) == 0 {
   393  		return res, fmt.Errorf("attempted to add 0 users to a team")
   394  	}
   395  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   396  		return res, err
   397  	}
   398  
   399  	added, notAdded, err := teams.AddMembers(ctx, h.G().ExternalG(), arg.TeamID, arg.Users, arg.EmailInviteMessage)
   400  	switch err := err.(type) {
   401  	case nil:
   402  	case teams.AddMembersError:
   403  		switch e := err.Err.(type) {
   404  		case libkb.IdentifySummaryError:
   405  			// Return the IdentifySummaryError, which is exportable.
   406  			// Frontend presents this error specifically.
   407  			return res, e
   408  		default:
   409  			return res, err
   410  		}
   411  	default:
   412  		return res, err
   413  	}
   414  	res = keybase1.TeamAddMembersResult{NotAdded: notAdded}
   415  
   416  	// AddMembers succeeded
   417  	if arg.SendChatNotification {
   418  		go func() {
   419  			h.G().Log.CDebugf(ctx, "sending team welcome messages")
   420  			ctx := libkb.WithLogTag(context.Background(), "BG")
   421  			for i, res := range added {
   422  				h.G().Log.CDebugf(ctx, "team welcome message for i:%v assertion:%v username:%v invite:%v, role: %v",
   423  					i, arg.Users[i].Assertion, res.Username, res.Invite, arg.Users[i].Role)
   424  				if !res.Invite && !res.Username.IsNil() {
   425  					err := teams.SendTeamChatWelcomeMessage(ctx, h.G().ExternalG(), arg.TeamID, "",
   426  						res.Username.String(), chat1.ConversationMembersType_TEAM, arg.Users[i].Role)
   427  					if err != nil {
   428  						h.G().Log.CDebugf(ctx, "send team welcome message [%v] err: %v", i, err)
   429  					} else {
   430  						h.G().Log.CDebugf(ctx, "send team welcome message [%v] success", i)
   431  					}
   432  				} else {
   433  					h.G().Log.CDebugf(ctx, "send team welcome message [%v] skipped", i)
   434  				}
   435  			}
   436  			h.G().Log.CDebugf(ctx, "done sending team welcome messages")
   437  		}()
   438  	}
   439  
   440  	var usernames []string
   441  	for _, user := range arg.Users {
   442  		// the server handles bot membership, skip these users
   443  		if !user.Role.IsBotLike() {
   444  			usernames = append(usernames, user.Assertion)
   445  		}
   446  	}
   447  	uid := gregor1.UID(h.G().GetMyUID().ToBytes())
   448  	for _, convIDStr := range arg.AddToChannels {
   449  		convID, err := chat1.MakeConvID(convIDStr)
   450  		if err != nil {
   451  			return res, err
   452  		}
   453  		err = h.G().ChatHelper.BulkAddToConv(ctx, uid, convID, usernames)
   454  		if err != nil {
   455  			return res, err
   456  		}
   457  	}
   458  	return res, nil
   459  }
   460  
   461  func (h *TeamsHandler) TeamRemoveMember(ctx context.Context, arg keybase1.TeamRemoveMemberArg) (err error) {
   462  	ctx = libkb.WithLogTag(ctx, "TM")
   463  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamRemoveMember(%s, %+v)", arg.TeamID, arg), &err)()
   464  
   465  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   466  		return err
   467  	}
   468  
   469  	return teams.RemoveMemberSingle(ctx, h.G().ExternalG(), arg.TeamID, arg.Member)
   470  }
   471  
   472  func (h *TeamsHandler) TeamRemoveMembers(ctx context.Context, arg keybase1.TeamRemoveMembersArg) (res keybase1.TeamRemoveMembersResult, err error) {
   473  	ctx = libkb.WithLogTag(ctx, "TM")
   474  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamRemoveMembers(%s, %v)", arg.TeamID, arg), &err)()
   475  	if len(arg.Members) == 0 {
   476  		return res, errors.New("no members provided to TeamRemoveMembers")
   477  	}
   478  
   479  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   480  		return res, err
   481  	}
   482  
   483  	return teams.RemoveMembers(ctx, h.G().ExternalG(), arg.TeamID, arg.Members, arg.NoErrorOnPartialFailure)
   484  }
   485  
   486  func (h *TeamsHandler) TeamEditMember(ctx context.Context, arg keybase1.TeamEditMemberArg) (err error) {
   487  	ctx = libkb.WithLogTag(ctx, "TM")
   488  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamEditMember(%s,%s,%s)", arg.Name, arg.Username, arg.Role),
   489  		&err)()
   490  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   491  		return err
   492  	}
   493  	return teams.EditMember(ctx, h.G().ExternalG(), arg.Name, arg.Username, arg.Role, arg.BotSettings)
   494  }
   495  
   496  func (h *TeamsHandler) TeamEditMembers(ctx context.Context, arg keybase1.TeamEditMembersArg) (res keybase1.TeamEditMembersResult, err error) {
   497  	ctx = libkb.WithLogTag(ctx, "TM")
   498  	debugString := "0"
   499  	if len(arg.Users) > 0 {
   500  		debugString = fmt.Sprintf("'%v, %v'", arg.Users[0].Assertion, arg.Users[0].Role)
   501  		if len(arg.Users) > 1 {
   502  			debugString = fmt.Sprintf("'%v' + %v more", arg.Users[0].Assertion, len(arg.Users)-1)
   503  		}
   504  	}
   505  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamEditMembers(%s, %s)", arg.TeamID, debugString),
   506  		&err)()
   507  	if len(arg.Users) == 0 {
   508  		return res, nil
   509  	}
   510  
   511  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   512  		return res, err
   513  	}
   514  
   515  	return teams.EditMembers(ctx, h.G().ExternalG(), arg.TeamID, arg.Users)
   516  }
   517  
   518  func (h *TeamsHandler) TeamSetBotSettings(ctx context.Context, arg keybase1.TeamSetBotSettingsArg) (err error) {
   519  	ctx = libkb.WithLogTag(ctx, "TM")
   520  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamSetBotSettings(%s,%s,%v)", arg.Name, arg.Username, arg.BotSettings),
   521  		&err)()
   522  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   523  		return err
   524  	}
   525  	return teams.SetBotSettings(ctx, h.G().ExternalG(), arg.Name, arg.Username, arg.BotSettings)
   526  }
   527  
   528  func (h *TeamsHandler) TeamGetBotSettings(ctx context.Context, arg keybase1.TeamGetBotSettingsArg) (res keybase1.TeamBotSettings, err error) {
   529  	ctx = libkb.WithLogTag(ctx, "TM")
   530  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamGetBotSettings(%s,%s)", arg.Name, arg.Username),
   531  		&err)()
   532  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   533  		return res, err
   534  	}
   535  	return teams.GetBotSettings(ctx, h.G().ExternalG(), arg.Name, arg.Username)
   536  }
   537  
   538  func (h *TeamsHandler) TeamLeave(ctx context.Context, arg keybase1.TeamLeaveArg) (err error) {
   539  	ctx = libkb.WithLogTag(ctx, "TM")
   540  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamLeave(%s)", arg.Name), &err)()
   541  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   542  		return err
   543  	}
   544  	return teams.Leave(ctx, h.G().ExternalG(), arg.Name, arg.Permanent)
   545  }
   546  
   547  func (h *TeamsHandler) TeamRename(ctx context.Context, arg keybase1.TeamRenameArg) (err error) {
   548  	ctx = libkb.WithLogTag(ctx, "TM")
   549  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamRename(%s)", arg.PrevName), &err)()
   550  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   551  		return err
   552  	}
   553  	return teams.RenameSubteam(ctx, h.G().ExternalG(), arg.PrevName, arg.NewName)
   554  }
   555  
   556  func (h *TeamsHandler) TeamAcceptInvite(ctx context.Context, arg keybase1.TeamAcceptInviteArg) (err error) {
   557  	ctx = libkb.WithLogTag(ctx, "TM")
   558  	defer h.G().CTrace(ctx, "TeamAcceptInvite", &err)()
   559  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   560  		return err
   561  	}
   562  
   563  	// If token looks at all like Seitan, don't pass to functions that might
   564  	// log or send to server.
   565  	parsedToken, wasSeitany := teams.ParseSeitanTokenFromPaste(arg.Token)
   566  	if wasSeitany {
   567  		mctx := h.MetaContext(ctx)
   568  		ui := h.getTeamsUI(arg.SessionID)
   569  		_, err = teams.ParseAndAcceptSeitanToken(mctx, ui, parsedToken)
   570  		return err
   571  	}
   572  
   573  	// Fallback to legacy email TOFU token
   574  	return teams.AcceptServerTrustInvite(ctx, h.G().ExternalG(), arg.Token)
   575  }
   576  
   577  func (h *TeamsHandler) TeamRequestAccess(ctx context.Context, arg keybase1.TeamRequestAccessArg) (res keybase1.TeamRequestAccessResult, err error) {
   578  	ctx = libkb.WithLogTag(ctx, "TM")
   579  	h.G().CTrace(ctx, "TeamRequestAccess", &err)()
   580  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   581  		return keybase1.TeamRequestAccessResult{}, err
   582  	}
   583  	return teams.RequestAccess(ctx, h.G().ExternalG(), arg.Name)
   584  }
   585  
   586  func (h *TeamsHandler) TeamAcceptInviteOrRequestAccess(ctx context.Context, arg keybase1.TeamAcceptInviteOrRequestAccessArg) (res keybase1.TeamAcceptOrRequestResult, err error) {
   587  	ctx = libkb.WithLogTag(ctx, "TM")
   588  	defer h.G().CTrace(ctx, "TeamAcceptInviteOrRequestAccess", &err)()
   589  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   590  		return res, err
   591  	}
   592  	ui := h.getTeamsUI(arg.SessionID)
   593  
   594  	// If token looks at all like Seitan, don't pass to functions that might log or send to server.
   595  	maybeSeitan, keepSecret := teams.ParseSeitanTokenFromPaste(arg.TokenOrName)
   596  	if keepSecret {
   597  		mctx := h.MetaContext(ctx)
   598  		_, err = teams.ParseAndAcceptSeitanToken(mctx, ui, maybeSeitan)
   599  		return keybase1.TeamAcceptOrRequestResult{WasSeitan: true, WasToken: true}, err
   600  	}
   601  	return teams.TeamAcceptInviteOrRequestAccess(ctx, h.G().ExternalG(), ui, arg.TokenOrName)
   602  }
   603  
   604  func (h *TeamsHandler) TeamListRequests(ctx context.Context, arg keybase1.TeamListRequestsArg) (res []keybase1.TeamJoinRequest, err error) {
   605  	ctx = libkb.WithLogTag(ctx, "TM")
   606  	defer h.G().CTrace(ctx, "TeamListRequests", &err)()
   607  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   608  		return nil, err
   609  	}
   610  	return teams.ListRequests(ctx, h.G().ExternalG(), arg.TeamName)
   611  }
   612  
   613  func (h *TeamsHandler) TeamListMyAccessRequests(ctx context.Context, arg keybase1.TeamListMyAccessRequestsArg) (res []keybase1.TeamName, err error) {
   614  	ctx = libkb.WithLogTag(ctx, "TM")
   615  	defer h.G().CTrace(ctx, "TeamListMyAccessRequests", &err)()
   616  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   617  		return nil, err
   618  	}
   619  	return teams.ListMyAccessRequests(ctx, h.G().ExternalG(), arg.TeamName)
   620  }
   621  
   622  func (h *TeamsHandler) TeamIgnoreRequest(ctx context.Context, arg keybase1.TeamIgnoreRequestArg) (err error) {
   623  	ctx = libkb.WithLogTag(ctx, "TM")
   624  	defer h.G().CTrace(ctx, "TeamIgnoreRequest", &err)()
   625  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   626  		return err
   627  	}
   628  	return teams.IgnoreRequest(ctx, h.G().ExternalG(), arg.Name, arg.Username)
   629  }
   630  
   631  func (h *TeamsHandler) TeamTreeUnverified(ctx context.Context, arg keybase1.TeamTreeUnverifiedArg) (res keybase1.TeamTreeResult, err error) {
   632  	ctx = libkb.WithLogTag(ctx, "TM")
   633  	defer h.G().CTrace(ctx, "TeamTreeUnverified", &err)()
   634  	return teams.TeamTreeUnverified(ctx, h.G().ExternalG(), arg)
   635  }
   636  
   637  func (h *TeamsHandler) TeamDelete(ctx context.Context, arg keybase1.TeamDeleteArg) (err error) {
   638  	ctx = libkb.WithLogTag(ctx, "TM")
   639  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamDelete(%s)", arg.TeamID), &err)()
   640  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   641  		return err
   642  	}
   643  	ui := h.getTeamsUI(arg.SessionID)
   644  	return teams.Delete(ctx, h.G().ExternalG(), ui, arg.TeamID)
   645  }
   646  
   647  func (h *TeamsHandler) TeamSetSettings(ctx context.Context, arg keybase1.TeamSetSettingsArg) (err error) {
   648  	ctx = libkb.WithLogTag(ctx, "TM")
   649  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamSetSettings(%s)", arg.TeamID), &err)()
   650  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   651  		return err
   652  	}
   653  	return teams.ChangeTeamSettingsByID(ctx, h.G().ExternalG(), arg.TeamID, arg.Settings)
   654  }
   655  
   656  func (h *TeamsHandler) LoadTeamPlusApplicationKeys(ctx context.Context, arg keybase1.LoadTeamPlusApplicationKeysArg) (res keybase1.TeamPlusApplicationKeys, err error) {
   657  	ctx = libkb.WithLogTag(ctx, "TM")
   658  	ctx = libkb.WithLogTag(ctx, "LTPAK")
   659  	defer h.G().CTrace(ctx, fmt.Sprintf("LoadTeamPlusApplicationKeys(%s)", arg.Id), &err)()
   660  
   661  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   662  	loader := func(mctx libkb.MetaContext) (interface{}, error) {
   663  		return teams.LoadTeamPlusApplicationKeys(ctx, h.G().ExternalG(), arg.Id, arg.Application, arg.Refreshers,
   664  			arg.IncludeKBFSKeys)
   665  	}
   666  
   667  	// argKey is a copy of arg that's going to be used for a cache key, so clear out
   668  	// refreshers and sessionID, since they don't affect the cache key value.
   669  	argKey := arg
   670  	argKey.Refreshers = keybase1.TeamRefreshers{}
   671  	argKey.SessionID = 0
   672  	argKey.IncludeKBFSKeys = false
   673  
   674  	servedRes, err := h.service.offlineRPCCache.Serve(mctx, arg.Oa, offline.Version(1), "teams.loadTeamPlusApplicationKeys", true, argKey, &res, loader)
   675  	if err != nil {
   676  		return keybase1.TeamPlusApplicationKeys{}, err
   677  	}
   678  	if s, ok := servedRes.(keybase1.TeamPlusApplicationKeys); ok {
   679  		res = s
   680  	}
   681  	return res, nil
   682  }
   683  
   684  func (h *TeamsHandler) TeamCreateSeitanToken(ctx context.Context, arg keybase1.TeamCreateSeitanTokenArg) (token keybase1.SeitanIKey, err error) {
   685  	ctx = libkb.WithLogTag(ctx, "TM")
   686  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   687  		return "", err
   688  	}
   689  	return teams.CreateSeitanToken(ctx, h.G().ExternalG(), arg.Teamname, arg.Role, arg.Label)
   690  }
   691  
   692  func (h *TeamsHandler) TeamCreateSeitanTokenV2(ctx context.Context, arg keybase1.TeamCreateSeitanTokenV2Arg) (token keybase1.SeitanIKeyV2, err error) {
   693  	ctx = libkb.WithLogTag(ctx, "TM")
   694  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   695  		return "", err
   696  	}
   697  	return teams.CreateSeitanTokenV2(ctx, h.G().ExternalG(), arg.Teamname, arg.Role, arg.Label)
   698  }
   699  
   700  func (h *TeamsHandler) TeamCreateSeitanInvitelink(ctx context.Context,
   701  	arg keybase1.TeamCreateSeitanInvitelinkArg) (invitelink keybase1.Invitelink, err error) {
   702  	ctx = libkb.WithLogTag(ctx, "TM")
   703  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   704  		return invitelink, err
   705  	}
   706  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   707  	return teams.CreateInvitelink(mctx, arg.Teamname, arg.Role, arg.MaxUses, arg.Etime)
   708  }
   709  
   710  func (h *TeamsHandler) TeamCreateSeitanInvitelinkWithDuration(ctx context.Context,
   711  	arg keybase1.TeamCreateSeitanInvitelinkWithDurationArg) (invitelink keybase1.Invitelink, err error) {
   712  	ctx = libkb.WithLogTag(ctx, "TM")
   713  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   714  		return invitelink, err
   715  	}
   716  
   717  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   718  
   719  	var etimeUnixPtr *keybase1.UnixTime
   720  	if arg.ExpireAfter != nil {
   721  		etime, err := kbtime.AddLongDuration(h.G().Clock().Now(), *arg.ExpireAfter)
   722  		if err != nil {
   723  			return invitelink, err
   724  		}
   725  		mctx.Debug("Etime from duration %q is: %s", arg.ExpireAfter, etime.String())
   726  		etimeUnix := keybase1.ToUnixTime(etime)
   727  		etimeUnixPtr = &etimeUnix
   728  	}
   729  
   730  	return teams.CreateInvitelink(mctx, arg.Teamname, arg.Role, arg.MaxUses, etimeUnixPtr)
   731  }
   732  
   733  func (h *TeamsHandler) GetTeamRootID(ctx context.Context, id keybase1.TeamID) (keybase1.TeamID, error) {
   734  	ctx = libkb.WithLogTag(ctx, "TM")
   735  	return teams.GetRootID(ctx, h.G().ExternalG(), id)
   736  }
   737  
   738  func (h *TeamsHandler) LookupImplicitTeam(ctx context.Context, arg keybase1.LookupImplicitTeamArg) (res keybase1.LookupImplicitTeamRes, err error) {
   739  	ctx = libkb.WithLogTag(ctx, "TM")
   740  	defer h.G().CTrace(ctx, fmt.Sprintf("LookupImplicitTeam(%s)", arg.Name), &err)()
   741  	var team *teams.Team
   742  	team, res.Name, res.DisplayName, err =
   743  		teams.LookupImplicitTeam(ctx, h.G().ExternalG(), arg.Name, arg.Public, teams.ImplicitTeamOptions{})
   744  	if err == nil {
   745  		res.TeamID = team.ID
   746  		res.TlfID = team.LatestKBFSTLFID()
   747  	}
   748  	return res, err
   749  }
   750  
   751  func (h *TeamsHandler) LookupOrCreateImplicitTeam(ctx context.Context, arg keybase1.LookupOrCreateImplicitTeamArg) (res keybase1.LookupImplicitTeamRes, err error) {
   752  	ctx = libkb.WithLogTag(ctx, "TM")
   753  	defer h.G().CTrace(ctx, fmt.Sprintf("LookupOrCreateImplicitTeam(%s)", arg.Name),
   754  		&err)()
   755  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   756  		return res, err
   757  	}
   758  	var team *teams.Team
   759  	team, res.Name, res.DisplayName, err = teams.LookupOrCreateImplicitTeam(ctx, h.G().ExternalG(),
   760  		arg.Name, arg.Public)
   761  	if err == nil {
   762  		res.TeamID = team.ID
   763  		res.TlfID = team.LatestKBFSTLFID()
   764  	}
   765  	return res, err
   766  }
   767  
   768  func (h *TeamsHandler) TeamReAddMemberAfterReset(ctx context.Context, arg keybase1.TeamReAddMemberAfterResetArg) (err error) {
   769  	ctx = libkb.WithLogTag(ctx, "TM")
   770  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamReAddMemberAfterReset(%s)", arg.Id), &err)()
   771  	if err := assertLoggedIn(ctx, h.G().ExternalG()); err != nil {
   772  		return err
   773  	}
   774  	return teams.ReAddMemberAfterReset(ctx, h.G().ExternalG(), arg.Id, arg.Username)
   775  }
   776  
   777  func (h *TeamsHandler) TeamAddEmailsBulk(ctx context.Context, arg keybase1.TeamAddEmailsBulkArg) (res keybase1.BulkRes, err error) {
   778  	ctx = libkb.WithLogTag(ctx, "TM")
   779  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamAddEmailsBulk(%s)", arg.Name), &err)()
   780  
   781  	return teams.AddEmailsBulk(ctx, h.G().ExternalG(), arg.Name, arg.Emails, arg.Role)
   782  }
   783  
   784  func (h *TeamsHandler) GetTeamShowcase(ctx context.Context, teamID keybase1.TeamID) (ret keybase1.TeamShowcase, err error) {
   785  	ctx = libkb.WithLogTag(ctx, "TM")
   786  	defer h.G().CTrace(ctx, fmt.Sprintf("GetTeamShowcase(%s)", teamID), &err)()
   787  
   788  	return teams.GetTeamShowcase(ctx, h.G().ExternalG(), teamID)
   789  }
   790  
   791  func (h *TeamsHandler) GetTeamAndMemberShowcase(ctx context.Context, id keybase1.TeamID) (ret keybase1.TeamAndMemberShowcase, err error) {
   792  	ctx = libkb.WithLogTag(ctx, "TM")
   793  	defer h.G().CTrace(ctx, fmt.Sprintf("GetTeamAndMemberShowcase(%s)", id), &err)()
   794  
   795  	return teams.GetTeamAndMemberShowcase(ctx, h.G().ExternalG(), id)
   796  }
   797  
   798  func (h *TeamsHandler) SetTeamShowcase(ctx context.Context, arg keybase1.SetTeamShowcaseArg) (err error) {
   799  	ctx = libkb.WithLogTag(ctx, "TM")
   800  	defer h.G().CTrace(ctx, fmt.Sprintf("SetTeamShowcase(%s)", arg.TeamID), &err)()
   801  
   802  	err = teams.SetTeamShowcase(ctx, h.G().ExternalG(), arg.TeamID, arg.IsShowcased, arg.Description, arg.AnyMemberShowcase)
   803  	return err
   804  }
   805  
   806  func (h *TeamsHandler) SetTeamMemberShowcase(ctx context.Context, arg keybase1.SetTeamMemberShowcaseArg) (err error) {
   807  	ctx = libkb.WithLogTag(ctx, "TM")
   808  	defer h.G().CTrace(ctx, fmt.Sprintf("SetTeamMemberShowcase(%s)", arg.TeamID), &err)()
   809  
   810  	err = teams.SetTeamMemberShowcase(ctx, h.G().ExternalG(), arg.TeamID, arg.IsShowcased)
   811  	return err
   812  }
   813  
   814  func (h *TeamsHandler) CanUserPerform(ctx context.Context, teamname string) (ret keybase1.TeamOperation, err error) {
   815  	ctx = libkb.WithLogTag(ctx, "TM")
   816  	defer h.G().CTrace(ctx, fmt.Sprintf("CanUserPerform(%s)", teamname), &err)()
   817  	// We never want to return an error from this, the frontend has no proper reaction to an error from
   818  	// this RPC call. We retry until we work.
   819  	for {
   820  		if ret, err = teams.CanUserPerform(ctx, h.G().ExternalG(), teamname); err == nil {
   821  			break
   822  		}
   823  		select {
   824  		case <-ctx.Done():
   825  			return ret, ctx.Err()
   826  		default:
   827  		}
   828  		time.Sleep(5 * time.Second)
   829  	}
   830  	return ret, err
   831  }
   832  
   833  func (h *TeamsHandler) TeamRotateKey(ctx context.Context, arg keybase1.TeamRotateKeyArg) (err error) {
   834  	ctx = libkb.WithLogTag(ctx, "TM")
   835  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamRotateKey(%v)", arg.TeamID), &err)()
   836  	return teams.RotateKey(ctx, h.G().ExternalG(), arg)
   837  }
   838  
   839  func (h *TeamsHandler) TeamDebug(ctx context.Context, teamID keybase1.TeamID) (res keybase1.TeamDebugRes, err error) {
   840  	ctx = libkb.WithLogTag(ctx, "TM")
   841  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamDebug(%v)", teamID), &err)()
   842  
   843  	return teams.TeamDebug(ctx, h.G().ExternalG(), teamID)
   844  }
   845  
   846  func (h *TeamsHandler) GetTarsDisabled(ctx context.Context, teamID keybase1.TeamID) (res bool, err error) {
   847  	ctx = libkb.WithLogTag(ctx, "TM")
   848  	defer h.G().CTrace(ctx, fmt.Sprintf("GetTarsDisabled(%s)", teamID), &err)()
   849  
   850  	return teams.GetTarsDisabled(ctx, h.G().ExternalG(), teamID)
   851  }
   852  
   853  func (h *TeamsHandler) SetTarsDisabled(ctx context.Context, arg keybase1.SetTarsDisabledArg) (err error) {
   854  	ctx = libkb.WithLogTag(ctx, "TM")
   855  	defer h.G().CTrace(ctx, fmt.Sprintf("SetTarsDisabled(%s,%t)", arg.TeamID, arg.Disabled), &err)()
   856  
   857  	return teams.SetTarsDisabled(ctx, h.G().ExternalG(), arg.TeamID, arg.Disabled)
   858  }
   859  
   860  func (h *TeamsHandler) TeamProfileAddList(ctx context.Context, arg keybase1.TeamProfileAddListArg) (res []keybase1.TeamProfileAddEntry, err error) {
   861  	ctx = libkb.WithLogTag(ctx, "TM")
   862  	defer h.G().CTrace(ctx, fmt.Sprintf("TeamProfileAddList(%v)", arg.Username), &err)()
   863  
   864  	return teams.TeamProfileAddList(ctx, h.G().ExternalG(), arg.Username)
   865  }
   866  
   867  func (h *TeamsHandler) UploadTeamAvatar(ctx context.Context, arg keybase1.UploadTeamAvatarArg) (err error) {
   868  	ctx = libkb.WithLogTag(ctx, "TM")
   869  	defer h.G().CTrace(ctx, fmt.Sprintf("UploadTeamAvatar(%s,%s)", arg.Teamname, arg.Filename), &err)()
   870  
   871  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   872  	return teams.ChangeTeamAvatar(mctx, arg)
   873  }
   874  
   875  func (h *TeamsHandler) TryDecryptWithTeamKey(ctx context.Context, arg keybase1.TryDecryptWithTeamKeyArg) (ret []byte, err error) {
   876  	ctx = libkb.WithLogTag(ctx, "TM")
   877  	defer h.G().CTrace(ctx, fmt.Sprintf("TryDecryptWithTeamKey(teamID:%s)", arg.TeamID), &err)()
   878  
   879  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   880  	return teams.TryDecryptWithTeamKey(mctx, arg)
   881  }
   882  
   883  func (h *TeamsHandler) FindNextMerkleRootAfterTeamRemoval(ctx context.Context, arg keybase1.FindNextMerkleRootAfterTeamRemovalArg) (res keybase1.NextMerkleRootRes, err error) {
   884  	ctx = libkb.WithLogTag(ctx, "TM")
   885  	defer h.G().CTrace(ctx, fmt.Sprintf("FindNextMerkleRootAfterTeamRemoval(%+v)", arg), &err)()
   886  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   887  	return libkb.FindNextMerkleRootAfterTeamRemoval(mctx, arg)
   888  }
   889  
   890  func (h *TeamsHandler) FindNextMerkleRootAfterTeamRemovalBySigningKey(ctx context.Context, arg keybase1.FindNextMerkleRootAfterTeamRemovalBySigningKeyArg) (res keybase1.NextMerkleRootRes, err error) {
   891  	ctx = libkb.WithLogTag(ctx, "TM")
   892  	defer h.G().CTrace(ctx, fmt.Sprintf("FindNextMerkleRootAfterTeamRemovalBySigningKey(%+v)", arg), &err)()
   893  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   894  	return teams.FindNextMerkleRootAfterRemoval(mctx, arg)
   895  }
   896  
   897  func (h *TeamsHandler) ProfileTeamLoad(ctx context.Context, arg keybase1.LoadTeamArg) (res keybase1.ProfileTeamLoadRes, err error) {
   898  	ctx = libkb.WithLogTag(ctx, "TM")
   899  	defer h.G().CTrace(ctx, fmt.Sprintf("ProfileTeamLoad(%+v)", arg), &err)()
   900  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   901  	return teams.ProfileTeamLoad(mctx, arg)
   902  }
   903  
   904  func (h *TeamsHandler) Ftl(ctx context.Context, arg keybase1.FastTeamLoadArg) (res keybase1.FastTeamLoadRes, err error) {
   905  	ctx = libkb.WithLogTag(ctx, "TM")
   906  	defer h.G().CTrace(ctx, fmt.Sprintf("Ftl(%+v)", arg), &err)()
   907  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   908  	return teams.FTL(mctx, arg)
   909  
   910  }
   911  
   912  func (h *TeamsHandler) GetTeamID(ctx context.Context, teamName string) (res keybase1.TeamID, err error) {
   913  	ctx = libkb.WithLogTag(ctx, "TM")
   914  	defer h.G().CTrace(ctx, fmt.Sprintf("GetTeamIDByName(%s)", teamName), &err)()
   915  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   916  	return teams.GetTeamIDByNameRPC(mctx, teamName)
   917  }
   918  
   919  func (h *TeamsHandler) GetTeamName(ctx context.Context, teamID keybase1.TeamID) (res keybase1.TeamName, err error) {
   920  	ctx = libkb.WithLogTag(ctx, "TM")
   921  	defer h.G().CTrace(ctx, fmt.Sprintf("GetTeamNameByID(%s)", teamID), &err)()
   922  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   923  	return teams.ResolveIDToName(mctx.Ctx(), mctx.G(), teamID)
   924  }
   925  
   926  func (h *TeamsHandler) GetTeamRoleMap(ctx context.Context) (res keybase1.TeamRoleMapAndVersion, err error) {
   927  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   928  	return mctx.G().GetTeamRoleMapManager().Get(mctx, true /* retry on fail */)
   929  }
   930  
   931  func (h *TeamsHandler) GetUntrustedTeamInfo(ctx context.Context, name keybase1.TeamName) (info keybase1.UntrustedTeamInfo, err error) {
   932  	ctx = libkb.WithLogTag(ctx, "TM")
   933  	defer h.G().CTrace(ctx, fmt.Sprintf("GetUntrustedTeamInfo(%s)", name), &err)()
   934  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   935  	return teams.GetUntrustedTeamInfo(mctx, name)
   936  }
   937  
   938  func (h *TeamsHandler) LoadTeamTreeMembershipsAsync(ctx context.Context,
   939  	arg keybase1.LoadTeamTreeMembershipsAsyncArg) (res keybase1.TeamTreeInitial, err error) {
   940  	ctx = libkb.WithLogTag(ctx, "TM")
   941  	ctx = libkb.WithLogTag(ctx, "TMTREE")
   942  	defer h.G().CTrace(ctx, fmt.Sprintf("LoadTeamTreeMembershipsAsync(%s, %s, %d)",
   943  		arg.TeamID, arg.Username, arg.SessionID), &err)()
   944  
   945  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   946  	loader, err := teams.NewTreeloader(mctx, arg.Username, arg.TeamID,
   947  		arg.SessionID, true /* includeAncestors */)
   948  	if err != nil {
   949  		return res, err
   950  	}
   951  	err = loader.LoadAsync(mctx)
   952  	if err != nil {
   953  		return res, err
   954  	}
   955  	return keybase1.TeamTreeInitial{Guid: arg.SessionID}, nil
   956  }
   957  
   958  func (h *TeamsHandler) GetInviteLinkDetails(ctx context.Context, inviteID keybase1.TeamInviteID) (details keybase1.InviteLinkDetails, err error) {
   959  	ctx = libkb.WithLogTag(ctx, "TM")
   960  	defer h.G().CTrace(ctx, fmt.Sprintf("GetInviteLinkDetails(%s)", inviteID), &err)()
   961  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   962  	return teams.GetInviteLinkDetails(mctx, inviteID)
   963  }
   964  
   965  func (h *TeamsHandler) FindAssertionsInTeamNoResolve(ctx context.Context, arg keybase1.FindAssertionsInTeamNoResolveArg) (ret []string, err error) {
   966  	ctx = libkb.WithLogTag(ctx, "TM")
   967  	traceMsg := fmt.Sprintf("FindAssertionsInTeam(%s, %d)", arg.TeamID, len(arg.Assertions))
   968  	defer h.G().CTrace(ctx, traceMsg, &err)()
   969  	mctx := libkb.NewMetaContext(ctx, h.G().ExternalG())
   970  	return teams.FindAssertionsInTeamNoResolve(mctx, arg.TeamID, arg.Assertions)
   971  }