github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/service/identify.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package service
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  
    10  	"golang.org/x/net/context"
    11  	"golang.org/x/sync/errgroup"
    12  
    13  	"github.com/keybase/client/go/engine"
    14  	"github.com/keybase/client/go/externals"
    15  	"github.com/keybase/client/go/libkb"
    16  	"github.com/keybase/client/go/offline"
    17  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    18  	"github.com/keybase/client/go/teams"
    19  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    20  )
    21  
    22  type RemoteIdentifyUI struct {
    23  	libkb.Contextified
    24  	sessionID  int
    25  	uicli      keybase1.IdentifyUiClient
    26  	logUI      libkb.LogUI
    27  	strict     bool
    28  	skipPrompt bool
    29  }
    30  
    31  var _ libkb.IdentifyUI = (*RemoteIdentifyUI)(nil)
    32  
    33  type IdentifyHandler struct {
    34  	*BaseHandler
    35  	libkb.Contextified
    36  	service *Service
    37  }
    38  
    39  func NewIdentifyHandler(xp rpc.Transporter, g *libkb.GlobalContext, s *Service) *IdentifyHandler {
    40  	return &IdentifyHandler{
    41  		BaseHandler:  NewBaseHandler(g, xp),
    42  		Contextified: libkb.NewContextified(g),
    43  		service:      s,
    44  	}
    45  }
    46  
    47  func (h *IdentifyHandler) Identify2(netCtx context.Context, arg keybase1.Identify2Arg) (res keybase1.Identify2Res, err error) {
    48  	netCtx = libkb.WithLogTag(netCtx, "ID2")
    49  	m := libkb.NewMetaContext(netCtx, h.G())
    50  	defer h.G().CTrace(netCtx, "IdentifyHandler#Identify2", &err)()
    51  
    52  	iui := h.NewRemoteIdentifyUI(arg.SessionID, h.G())
    53  	logui := h.getLogUI(arg.SessionID)
    54  	uis := libkb.UIs{
    55  		LogUI:      logui,
    56  		IdentifyUI: iui,
    57  		SessionID:  arg.SessionID,
    58  	}
    59  	m = m.WithUIs(uis)
    60  	eng := engine.NewResolveThenIdentify2(h.G(), &arg)
    61  	err = engine.RunEngine2(m, eng)
    62  	if err != nil {
    63  		return res, err
    64  	}
    65  	resp, err := eng.Result(m)
    66  	if err != nil {
    67  		return res, err
    68  	}
    69  	if resp != nil {
    70  		res = resp.ExportToV1()
    71  	}
    72  	return res, err
    73  }
    74  
    75  func (h *IdentifyHandler) IdentifyLite(netCtx context.Context, arg keybase1.IdentifyLiteArg) (ret keybase1.IdentifyLiteRes, err error) {
    76  	mctx := libkb.NewMetaContext(netCtx, h.G()).WithLogTag("IDL")
    77  	defer mctx.Trace("IdentifyHandler#IdentifyLite", &err)()
    78  	loader := func(mctx libkb.MetaContext) (interface{}, error) {
    79  		return h.identifyLite(mctx, arg)
    80  	}
    81  	cacheArg := keybase1.IdentifyLiteArg{
    82  		Id:               arg.Id,
    83  		Assertion:        arg.Assertion,
    84  		IdentifyBehavior: arg.IdentifyBehavior,
    85  	}
    86  	servedRet, err := h.service.offlineRPCCache.Serve(mctx, arg.Oa, offline.Version(1), "identify.identifyLite", false, cacheArg, &ret, loader)
    87  	if err != nil {
    88  		if s, ok := servedRet.(keybase1.IdentifyLiteRes); ok {
    89  			ret = s
    90  		}
    91  		return ret, err
    92  	}
    93  	if s, ok := servedRet.(keybase1.IdentifyLiteRes); ok {
    94  		ret = s
    95  	}
    96  	return ret, nil
    97  }
    98  
    99  func (h *IdentifyHandler) identifyLite(mctx libkb.MetaContext, arg keybase1.IdentifyLiteArg) (res keybase1.IdentifyLiteRes, err error) {
   100  
   101  	var au libkb.AssertionURL
   102  	var parseError error
   103  	if len(arg.Assertion) > 0 {
   104  		// It's OK to fail this assertion; it will be off in the case of regular lookups
   105  		// for users like `t_ellen` without a `type` specification
   106  		au, parseError = libkb.ParseAssertionURL(mctx.G().MakeAssertionContext(mctx), arg.Assertion, true)
   107  	} else {
   108  		// empty assertion url required for teams.IdentifyLite
   109  		au = libkb.AssertionKeybase{}
   110  	}
   111  
   112  	if arg.Id.IsTeamOrSubteam() || libkb.AssertionIsTeam(au) {
   113  		// Now heed the parse error.
   114  		if parseError != nil {
   115  			return res, parseError
   116  		}
   117  		return teams.IdentifyLite(mctx.Ctx(), mctx.G(), arg, au)
   118  	}
   119  
   120  	return h.identifyLiteUser(mctx.Ctx(), arg)
   121  }
   122  
   123  func (h *IdentifyHandler) identifyLiteUser(netCtx context.Context, arg keybase1.IdentifyLiteArg) (res keybase1.IdentifyLiteRes, err error) {
   124  	m := libkb.NewMetaContext(netCtx, h.G())
   125  	m.Debug("IdentifyLite on user")
   126  
   127  	var uid keybase1.UID
   128  	if arg.Id.Exists() {
   129  		uid, err = arg.Id.AsUser()
   130  		if err != nil {
   131  			return res, err
   132  		}
   133  	}
   134  
   135  	id2arg := keybase1.Identify2Arg{
   136  		SessionID:             arg.SessionID,
   137  		Uid:                   uid,
   138  		UserAssertion:         arg.Assertion,
   139  		Reason:                arg.Reason,
   140  		UseDelegateUI:         arg.UseDelegateUI,
   141  		AlwaysBlock:           arg.AlwaysBlock,
   142  		NoErrorOnTrackFailure: arg.NoErrorOnTrackFailure,
   143  		ForceRemoteCheck:      arg.ForceRemoteCheck,
   144  		NeedProofSet:          arg.NeedProofSet,
   145  		AllowEmptySelfID:      arg.AllowEmptySelfID,
   146  		NoSkipSelf:            arg.NoSkipSelf,
   147  		CanSuppressUI:         arg.CanSuppressUI,
   148  		IdentifyBehavior:      arg.IdentifyBehavior,
   149  		ForceDisplay:          arg.ForceDisplay,
   150  	}
   151  
   152  	iui := h.NewRemoteIdentifyUI(arg.SessionID, h.G())
   153  	logui := h.getLogUI(arg.SessionID)
   154  	uis := libkb.UIs{
   155  		LogUI:      logui,
   156  		IdentifyUI: iui,
   157  		SessionID:  arg.SessionID,
   158  	}
   159  	m = m.WithUIs(uis)
   160  	eng := engine.NewResolveThenIdentify2(h.G(), &id2arg)
   161  	err = engine.RunEngine2(m, eng)
   162  	if err != nil {
   163  		return res, err
   164  	}
   165  	resp, err := eng.Result(m)
   166  	if err != nil {
   167  		return res, err
   168  	}
   169  	res.Ul.Id = keybase1.UserOrTeamID(resp.Upk.GetUID())
   170  	res.Ul.Name = resp.Upk.GetName()
   171  	res.TrackBreaks = resp.TrackBreaks
   172  	return res, err
   173  }
   174  
   175  func (h *IdentifyHandler) Resolve3(ctx context.Context, arg keybase1.Resolve3Arg) (ret keybase1.UserOrTeamLite, err error) {
   176  	mctx := libkb.NewMetaContext(ctx, h.G()).WithLogTag("RSLV")
   177  	defer mctx.Trace(fmt.Sprintf("IdentifyHandler#Resolve3(%+v)", arg), &err)()
   178  	servedRet, err := h.service.offlineRPCCache.Serve(mctx, arg.Oa, offline.Version(1), "identify.resolve3", false, arg, &ret, func(mctx libkb.MetaContext) (interface{}, error) {
   179  		return h.resolveUserOrTeam(mctx.Ctx(), arg.Assertion)
   180  	})
   181  	if err != nil {
   182  		return keybase1.UserOrTeamLite{}, err
   183  	}
   184  	if s, ok := servedRet.(keybase1.UserOrTeamLite); ok {
   185  		ret = s
   186  	}
   187  	return ret, nil
   188  }
   189  
   190  func (h *IdentifyHandler) resolveUserOrTeam(ctx context.Context, arg string) (u keybase1.UserOrTeamLite, err error) {
   191  
   192  	res := h.G().Resolver.ResolveFullExpressionNeedUsername(libkb.NewMetaContext(ctx, h.G()), arg)
   193  	err = res.GetError()
   194  	if err != nil {
   195  		return u, err
   196  	}
   197  	return res.UserOrTeam(), nil
   198  }
   199  
   200  func (h *IdentifyHandler) ResolveIdentifyImplicitTeam(ctx context.Context, arg keybase1.ResolveIdentifyImplicitTeamArg) (res keybase1.ResolveIdentifyImplicitTeamRes, err error) {
   201  	mctx := libkb.NewMetaContext(ctx, h.G()).WithLogTag("RIIT")
   202  	defer mctx.Trace(fmt.Sprintf("IdentifyHandler#ResolveIdentifyImplicitTeam(%+v)", arg), &err)()
   203  
   204  	writerAssertions, readerAssertions, err := externals.ParseAssertionsWithReaders(h.MetaContext(ctx), arg.Assertions)
   205  	if err != nil {
   206  		return res, err
   207  	}
   208  
   209  	cacheArg := keybase1.ResolveIdentifyImplicitTeamArg{
   210  		Assertions: arg.Assertions,
   211  		Suffix:     arg.Suffix,
   212  		IsPublic:   arg.IsPublic,
   213  	}
   214  
   215  	servedRes, err := h.service.offlineRPCCache.Serve(mctx, arg.Oa, offline.Version(1), "identify.resolveIdentifyImplicitTeam", false, cacheArg, &res, func(mctx libkb.MetaContext) (interface{}, error) {
   216  		return h.resolveIdentifyImplicitTeamHelper(mctx.Ctx(), arg, writerAssertions, readerAssertions)
   217  	})
   218  
   219  	if s, ok := servedRes.(keybase1.ResolveIdentifyImplicitTeamRes); ok {
   220  		// We explicitly want to return `servedRes` here, when err !=
   221  		// nil, as the caller might depend on it in certain cases.
   222  		res = s
   223  	} else if err != nil {
   224  		res = keybase1.ResolveIdentifyImplicitTeamRes{}
   225  	}
   226  	mctx.Debug("res: {displayName: %s, teamID: %s, folderID: %s}", res.DisplayName, res.TeamID, res.FolderID)
   227  	return res, err
   228  }
   229  
   230  func (h *IdentifyHandler) resolveIdentifyImplicitTeamHelper(ctx context.Context, arg keybase1.ResolveIdentifyImplicitTeamArg,
   231  	writerAssertions, readerAssertions []libkb.AssertionExpression) (res keybase1.ResolveIdentifyImplicitTeamRes, err error) {
   232  
   233  	lookupName := keybase1.ImplicitTeamDisplayName{
   234  		IsPublic: arg.IsPublic,
   235  	}
   236  	if len(arg.Suffix) > 0 {
   237  		lookupName.ConflictInfo, err = libkb.ParseImplicitTeamDisplayNameSuffix(arg.Suffix)
   238  		if err != nil {
   239  			return res, err
   240  		}
   241  	}
   242  
   243  	var resolvedAssertions []libkb.ResolvedAssertion
   244  
   245  	err = teams.ResolveImplicitTeamSetUntrusted(ctx, h.G(), writerAssertions, &lookupName.Writers, &resolvedAssertions)
   246  	if err != nil {
   247  		return res, err
   248  	}
   249  	err = teams.ResolveImplicitTeamSetUntrusted(ctx, h.G(), readerAssertions, &lookupName.Readers, &resolvedAssertions)
   250  	if err != nil {
   251  		return res, err
   252  	}
   253  
   254  	lookupNameStr, err := teams.FormatImplicitTeamDisplayName(ctx, h.G(), lookupName)
   255  	if err != nil {
   256  		return res, err
   257  	}
   258  	h.G().Log.CDebugf(ctx, "ResolveIdentifyImplicitTeam looking up:'%v'", lookupNameStr)
   259  
   260  	var team *teams.Team
   261  	var impName keybase1.ImplicitTeamDisplayName
   262  	// Lookup*ImplicitTeam is responsible for making sure the returned team has the members from lookupName.
   263  	// Duplicates are also handled by Lookup*. So we might end up doing extra identifies of duplicates out here.
   264  	// (Duplicates e.g. "me,chris,chris", "me,chris#chris", "me,chris@rooter#chris")
   265  	if arg.Create {
   266  		team, _, impName, err = teams.LookupOrCreateImplicitTeam(ctx, h.G(), lookupNameStr, arg.IsPublic)
   267  	} else {
   268  		team, _, impName, err = teams.LookupImplicitTeam(ctx, h.G(), lookupNameStr, arg.IsPublic, teams.ImplicitTeamOptions{})
   269  	}
   270  	if err != nil {
   271  		return res, err
   272  	}
   273  
   274  	// can be nil
   275  	myUID := h.G().ActiveDevice.UID()
   276  
   277  	var displayNameKBFS string
   278  	if myUID.Exists() {
   279  		var name libkb.NormalizedUsername
   280  		name, err = h.G().GetUPAKLoader().LookupUsername(ctx, myUID)
   281  		if err != nil {
   282  			return res, err
   283  		}
   284  		// display name with the logged-in user first
   285  		displayNameKBFS, err = teams.FormatImplicitTeamDisplayNameWithUserFront(ctx, h.G(), impName, name)
   286  	} else {
   287  		displayNameKBFS, err = teams.FormatImplicitTeamDisplayName(ctx, h.G(), impName)
   288  	}
   289  	if err != nil {
   290  		return res, err
   291  	}
   292  
   293  	writers, err := team.UsersWithRoleOrAbove(keybase1.TeamRole_WRITER)
   294  	if err != nil {
   295  		return res, err
   296  	}
   297  
   298  	// Populate the result. It may get returned together with an identify error.
   299  	res = keybase1.ResolveIdentifyImplicitTeamRes{
   300  		DisplayName: displayNameKBFS,
   301  		TeamID:      team.ID,
   302  		Writers:     writers,
   303  		TrackBreaks: nil,
   304  		FolderID:    team.LatestKBFSTLFID(),
   305  	}
   306  
   307  	if arg.DoIdentifies {
   308  		return h.resolveIdentifyImplicitTeamDoIdentifies(ctx, arg, res, resolvedAssertions)
   309  	}
   310  	return res, nil
   311  }
   312  
   313  func (h *IdentifyHandler) resolveIdentifyImplicitTeamDoIdentifies(ctx context.Context, arg keybase1.ResolveIdentifyImplicitTeamArg,
   314  	res keybase1.ResolveIdentifyImplicitTeamRes, resolvedAssertions []libkb.ResolvedAssertion) (keybase1.ResolveIdentifyImplicitTeamRes, error) {
   315  
   316  	// errgroup collects errors and returns the first non-nil.
   317  	// subctx is canceled when the group finishes.
   318  	group, subctx := errgroup.WithContext(ctx)
   319  
   320  	// lock guarding res.TrackBreaks
   321  	var trackBreaksLock sync.Mutex
   322  
   323  	var okUsernames, brokenUsernames []string
   324  
   325  	// Identify everyone who resolved in parallel, checking that they match their resolved UID and original assertions.
   326  	for _, resolvedAssertion := range resolvedAssertions {
   327  		resolvedAssertion := resolvedAssertion // https://golang.org/doc/faq#closures_and_goroutines
   328  		group.Go(func() error {
   329  			h.G().Log.CDebugf(ctx, "ResolveIdentifyImplicitTeam ID user [%s] %s", resolvedAssertion.UID, resolvedAssertion.Assertion.String())
   330  
   331  			id2arg := keybase1.Identify2Arg{
   332  				SessionID:             arg.SessionID,
   333  				Uid:                   resolvedAssertion.UID,
   334  				UserAssertion:         resolvedAssertion.Assertion.String(),
   335  				Reason:                arg.Reason,
   336  				UseDelegateUI:         true,
   337  				AlwaysBlock:           false,
   338  				NoErrorOnTrackFailure: false,
   339  				ForceRemoteCheck:      false,
   340  				NeedProofSet:          false,
   341  				AllowEmptySelfID:      false,
   342  				NoSkipSelf:            false,
   343  				CanSuppressUI:         true,
   344  				IdentifyBehavior:      arg.IdentifyBehavior,
   345  				ForceDisplay:          false,
   346  			}
   347  
   348  			iui := h.NewRemoteIdentifyUI(arg.SessionID, h.G())
   349  			logui := h.getLogUI(arg.SessionID)
   350  			uis := libkb.UIs{
   351  				LogUI:      logui,
   352  				IdentifyUI: iui,
   353  				SessionID:  arg.SessionID,
   354  			}
   355  
   356  			eng := engine.NewIdentify2WithUID(h.G(), &id2arg)
   357  			m := libkb.NewMetaContext(subctx, h.G()).WithUIs(uis)
   358  			err := engine.RunEngine2(m, eng)
   359  			idRes, idErr := eng.Result(m)
   360  			if idErr != nil {
   361  				h.G().Log.CDebugf(subctx, "Failed to convert result from Identify2: %s", idErr)
   362  			}
   363  			if idRes != nil {
   364  				trackBreaksLock.Lock()
   365  				defer trackBreaksLock.Unlock()
   366  				if idRes.TrackBreaks != nil && idErr == nil {
   367  					if res.TrackBreaks == nil {
   368  						res.TrackBreaks = make(map[keybase1.UserVersion]keybase1.IdentifyTrackBreaks)
   369  					}
   370  					res.TrackBreaks[idRes.Upk.ToUserVersion()] = *idRes.TrackBreaks
   371  					brokenUsernames = append(brokenUsernames, idRes.Upk.GetName())
   372  				} else {
   373  					okUsernames = append(okUsernames, idRes.Upk.GetName())
   374  				}
   375  			}
   376  			if err != nil {
   377  				h.G().Log.CDebugf(subctx, "identify failed (IDres %v, TrackBreaks %v): %v", idRes != nil, idRes != nil && idRes.TrackBreaks != nil, err)
   378  				return err
   379  			}
   380  			return nil
   381  		})
   382  	}
   383  
   384  	err := group.Wait()
   385  	if err != nil {
   386  		// Return the masked error together with a populated response.
   387  		return res, libkb.NewIdentifiesFailedError()
   388  	}
   389  
   390  	if arg.IdentifyBehavior.NotifyGUIAboutBreaks() && len(res.TrackBreaks) > 0 {
   391  		h.G().NotifyRouter.HandleIdentifyUpdate(ctx, okUsernames, brokenUsernames)
   392  	}
   393  
   394  	return res, err
   395  }
   396  
   397  func (h *IdentifyHandler) ResolveImplicitTeam(ctx context.Context, arg keybase1.ResolveImplicitTeamArg) (res keybase1.Folder, err error) {
   398  	ctx = libkb.WithLogTag(ctx, "RIT")
   399  	defer h.G().CTrace(ctx, fmt.Sprintf("ResolveImplicitTeam(%s)", arg.Id), &err)()
   400  	return teams.MapImplicitTeamIDToDisplayName(ctx, h.G(), arg.Id, arg.Id.IsPublic())
   401  }
   402  
   403  func (h *IdentifyHandler) NormalizeSocialAssertion(ctx context.Context, assertion string) (socialAssertion keybase1.SocialAssertion, err error) {
   404  	ctx = libkb.WithLogTag(ctx, "NSA")
   405  	defer h.G().CTrace(ctx, fmt.Sprintf("IdentifyHandler#NormalizeSocialAssertion(%s)", assertion), &err)()
   406  	socialAssertion, isSocialAssertion := libkb.NormalizeSocialAssertion(h.G().MakeAssertionContext(h.MetaContext(ctx)), assertion)
   407  	if !isSocialAssertion {
   408  		return keybase1.SocialAssertion{}, fmt.Errorf("Invalid social assertion")
   409  	}
   410  	return socialAssertion, nil
   411  }
   412  
   413  func (u *RemoteIdentifyUI) newMetaContext(mctx libkb.MetaContext) (libkb.MetaContext, func()) {
   414  	return mctx.WithTimeout(libkb.RemoteIdentifyUITimeout)
   415  }
   416  
   417  func (u *RemoteIdentifyUI) FinishWebProofCheck(mctx libkb.MetaContext, p keybase1.RemoteProof, lcr keybase1.LinkCheckResult) error {
   418  	mctx, cancel := u.newMetaContext(mctx)
   419  	defer cancel()
   420  	return u.uicli.FinishWebProofCheck(mctx.Ctx(), keybase1.FinishWebProofCheckArg{
   421  		SessionID: u.sessionID,
   422  		Rp:        p,
   423  		Lcr:       lcr,
   424  	})
   425  }
   426  
   427  func (u *RemoteIdentifyUI) FinishSocialProofCheck(mctx libkb.MetaContext, p keybase1.RemoteProof, lcr keybase1.LinkCheckResult) error {
   428  	mctx, cancel := u.newMetaContext(mctx)
   429  	defer cancel()
   430  	return u.uicli.FinishSocialProofCheck(mctx.Ctx(), keybase1.FinishSocialProofCheckArg{
   431  		SessionID: u.sessionID,
   432  		Rp:        p,
   433  		Lcr:       lcr,
   434  	})
   435  }
   436  
   437  func (u *RemoteIdentifyUI) Confirm(mctx libkb.MetaContext, io *keybase1.IdentifyOutcome) (keybase1.ConfirmResult, error) {
   438  	if u.skipPrompt {
   439  		mctx.Debug("skipping Confirm for %q", io.Username)
   440  		return keybase1.ConfirmResult{IdentityConfirmed: true}, nil
   441  	}
   442  	return u.uicli.Confirm(mctx.Ctx(), keybase1.ConfirmArg{SessionID: u.sessionID, Outcome: *io})
   443  }
   444  
   445  func (u *RemoteIdentifyUI) DisplayCryptocurrency(mctx libkb.MetaContext, c keybase1.Cryptocurrency) error {
   446  	mctx, cancel := u.newMetaContext(mctx)
   447  	defer cancel()
   448  	return u.uicli.DisplayCryptocurrency(mctx.Ctx(), keybase1.DisplayCryptocurrencyArg{SessionID: u.sessionID, C: c})
   449  }
   450  
   451  func (u *RemoteIdentifyUI) DisplayStellarAccount(mctx libkb.MetaContext, a keybase1.StellarAccount) error {
   452  	mctx, cancel := u.newMetaContext(mctx)
   453  	defer cancel()
   454  	return u.uicli.DisplayStellarAccount(mctx.Ctx(), keybase1.DisplayStellarAccountArg{SessionID: u.sessionID, A: a})
   455  }
   456  
   457  func (u *RemoteIdentifyUI) DisplayKey(mctx libkb.MetaContext, key keybase1.IdentifyKey) error {
   458  	mctx, cancel := u.newMetaContext(mctx)
   459  	defer cancel()
   460  	return u.uicli.DisplayKey(mctx.Ctx(), keybase1.DisplayKeyArg{SessionID: u.sessionID, Key: key})
   461  }
   462  
   463  func (u *RemoteIdentifyUI) ReportLastTrack(mctx libkb.MetaContext, t *keybase1.TrackSummary) error {
   464  	mctx, cancel := u.newMetaContext(mctx)
   465  	defer cancel()
   466  	return u.uicli.ReportLastTrack(mctx.Ctx(), keybase1.ReportLastTrackArg{SessionID: u.sessionID, Track: t})
   467  }
   468  
   469  func (u *RemoteIdentifyUI) DisplayTrackStatement(mctx libkb.MetaContext, s string) error {
   470  	mctx, cancel := u.newMetaContext(mctx)
   471  	defer cancel()
   472  	return u.uicli.DisplayTrackStatement(mctx.Ctx(), keybase1.DisplayTrackStatementArg{Stmt: s, SessionID: u.sessionID})
   473  }
   474  
   475  func (u *RemoteIdentifyUI) ReportTrackToken(mctx libkb.MetaContext, token keybase1.TrackToken) error {
   476  	mctx, cancel := u.newMetaContext(mctx)
   477  	defer cancel()
   478  	return u.uicli.ReportTrackToken(mctx.Ctx(), keybase1.ReportTrackTokenArg{TrackToken: token, SessionID: u.sessionID})
   479  }
   480  
   481  func (u *RemoteIdentifyUI) LaunchNetworkChecks(mctx libkb.MetaContext, id *keybase1.Identity, user *keybase1.User) error {
   482  	mctx, cancel := u.newMetaContext(mctx)
   483  	defer cancel()
   484  	return u.uicli.LaunchNetworkChecks(mctx.Ctx(), keybase1.LaunchNetworkChecksArg{
   485  		SessionID: u.sessionID,
   486  		Identity:  *id,
   487  		User:      *user,
   488  	})
   489  }
   490  
   491  func (u *RemoteIdentifyUI) DisplayUserCard(mctx libkb.MetaContext, card keybase1.UserCard) error {
   492  	mctx, cancel := u.newMetaContext(mctx)
   493  	defer cancel()
   494  	return u.uicli.DisplayUserCard(mctx.Ctx(), keybase1.DisplayUserCardArg{SessionID: u.sessionID, Card: card})
   495  }
   496  
   497  func (u *RemoteIdentifyUI) Start(mctx libkb.MetaContext, username string, reason keybase1.IdentifyReason, force bool) error {
   498  	mctx, cancel := u.newMetaContext(mctx)
   499  	defer cancel()
   500  	return u.uicli.Start(mctx.Ctx(), keybase1.StartArg{SessionID: u.sessionID, Username: username, Reason: reason, ForceDisplay: force})
   501  }
   502  
   503  func (u *RemoteIdentifyUI) Cancel(mctx libkb.MetaContext) error {
   504  	if u.uicli.Cli == nil {
   505  		return nil
   506  	}
   507  	mctx, cancel := u.newMetaContext(mctx)
   508  	defer cancel()
   509  	return u.uicli.Cancel(mctx.Ctx(), u.sessionID)
   510  }
   511  
   512  func (u *RemoteIdentifyUI) Finish(mctx libkb.MetaContext) error {
   513  	mctx, cancel := u.newMetaContext(mctx)
   514  	defer cancel()
   515  	return u.uicli.Finish(mctx.Ctx(), u.sessionID)
   516  }
   517  
   518  func (u *RemoteIdentifyUI) Dismiss(mctx libkb.MetaContext, username string, reason keybase1.DismissReason) error {
   519  	mctx, cancel := u.newMetaContext(mctx)
   520  	defer cancel()
   521  	return u.uicli.Dismiss(mctx.Ctx(), keybase1.DismissArg{
   522  		SessionID: u.sessionID,
   523  		Username:  username,
   524  		Reason:    reason,
   525  	})
   526  }
   527  
   528  func (u *RemoteIdentifyUI) SetStrict(b bool) {
   529  	u.strict = b
   530  }
   531  
   532  func (u *RemoteIdentifyUI) DisplayTLFCreateWithInvite(mctx libkb.MetaContext, arg keybase1.DisplayTLFCreateWithInviteArg) error {
   533  	mctx, cancel := u.newMetaContext(mctx)
   534  	defer cancel()
   535  	arg.SessionID = u.sessionID
   536  	return u.uicli.DisplayTLFCreateWithInvite(mctx.Ctx(), arg)
   537  }