github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/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 }