/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/teams/resolve.go
267 строк
9 KB
zoom-ua
modernize error handling with errors.As/Is (#29350)
25 июн 2026, 17:41
Не верифицирован
25 июн 2026, 17:41
e09e254
Код
Авторство
О чём код?
package teams import ( "context" "errors" "fmt" "strings" "golang.org/x/sync/errgroup" "github.com/keybase/client/go/engine" "github.com/keybase/client/go/externals" "github.com/keybase/client/go/libkb" keybase1 "github.com/keybase/client/go/protocol/keybase1" ) // ResolveIDToName takes a team ID and resolves it to a name. // It can use server-assist but always cryptographically checks the result. func ResolveIDToName(ctx context.Context, g *libkb.GlobalContext, id keybase1.TeamID) (name keybase1.TeamName, err error) { return resolveIDToName(ctx, g, id, false) } // ResolveIDToNameForceRefresh is like ResolveIDToName but forces a refresh of // the FTL cache. func ResolveIDToNameForceRefresh(ctx context.Context, g *libkb.GlobalContext, id keybase1.TeamID) (name keybase1.TeamName, err error) { return resolveIDToName(ctx, g, id, true) } func resolveIDToName(ctx context.Context, g *libkb.GlobalContext, id keybase1.TeamID, forceRefresh bool) (name keybase1.TeamName, err error) { m := libkb.NewMetaContext(ctx, g) rres := g.Resolver.ResolveFullExpression(m, fmt.Sprintf("tid:%s", id)) if err = rres.GetError(); err != nil { return keybase1.TeamName{}, err } name = rres.GetTeamName() if err = g.GetFastTeamLoader().VerifyTeamName(m, id, name, forceRefresh); err != nil { return keybase1.TeamName{}, err } return name, nil } // ResolveNameToID takes a team name and resolves it to a team ID. // It can use server-assist but always cryptographically checks the result. func ResolveNameToID(ctx context.Context, g *libkb.GlobalContext, name keybase1.TeamName) (id keybase1.TeamID, err error) { return resolveNameToID(ctx, g, name, false) } // ResolveNameToIDForceRefresh is just like ResolveNameToID but it forces a refresh. func ResolveNameToIDForceRefresh(ctx context.Context, g *libkb.GlobalContext, name keybase1.TeamName) (id keybase1.TeamID, err error) { return resolveNameToID(ctx, g, name, true) } func resolveNameToID(ctx context.Context, g *libkb.GlobalContext, name keybase1.TeamName, forceRefresh bool) (id keybase1.TeamID, err error) { m := libkb.NewMetaContext(ctx, g) rres := g.Resolver.ResolveFullExpression(m, fmt.Sprintf("team:%s", name)) if err = rres.GetError(); err != nil { return keybase1.TeamID(""), err } id = rres.GetTeamID() if err = g.GetFastTeamLoader().VerifyTeamName(m, id, name, forceRefresh); err != nil { return keybase1.TeamID(""), err } return id, nil } func PurgeResolverTeamID(ctx context.Context, g *libkb.GlobalContext, teamID keybase1.TeamID) error { m := libkb.NewMetaContext(ctx, g) return g.Resolver.PurgeResolveCache(m, fmt.Sprintf("tid:%s", teamID)) } // Resolve assertions in an implicit team display name and verify the result. // Resolve an implicit team name with assertions like "alice,bob+bob@twitter#char (conflicted copy 2017-03-04 #1)" // Into "alice,bob#char (conflicted copy 2017-03-04 #1)" // The input can contain compound assertions, but if compound assertions are left unresolved, an error will be returned. func ResolveImplicitTeamDisplayName(ctx context.Context, g *libkb.GlobalContext, name string, public bool, ) (res keybase1.ImplicitTeamDisplayName, err error) { defer g.CTrace(ctx, fmt.Sprintf("ResolveImplicitTeamDisplayName(%v, public:%v)", name, public), &err)() split1 := strings.SplitN(name, " ", 2) // split1: [assertions, ?conflict] assertions := split1[0] var suffix string if len(split1) > 1 { suffix = split1[1] } writerAssertions, readerAssertions, err := externals.ParseAssertionsWithReaders(libkb.NewMetaContext(ctx, g), assertions) if err != nil { return res, err } res = keybase1.ImplicitTeamDisplayName{ IsPublic: public, } if len(suffix) > 0 { res.ConflictInfo, err = libkb.ParseImplicitTeamDisplayNameSuffix(suffix) if err != nil { return res, err } } var resolvedAssertions []libkb.ResolvedAssertion if err = ResolveImplicitTeamSetUntrusted(ctx, g, writerAssertions, &res.Writers, &resolvedAssertions); err != nil { return res, err } if err = ResolveImplicitTeamSetUntrusted(ctx, g, readerAssertions, &res.Readers, &resolvedAssertions); err != nil { return res, err } deduplicateImplicitTeamDisplayName(&res) // errgroup collects errors and returns the first non-nil. // subctx is canceled when the group finishes. group, subctx := errgroup.WithContext(ctx) // Identify everyone who resolved in parallel, checking that they match their resolved UID and original assertions. for _, resolvedAssertion := range resolvedAssertions { group.Go(func() error { return verifyResolveResult(subctx, g, resolvedAssertion) }) } err = group.Wait() return res, err } // preventTeamCreationOnError checks if an error coming from resolver should // prevent us from creating a team. We don't want a team where we don't know if // SBS user is resolvable but we just were unable to get the answer. func shouldPreventTeamCreation(err error) bool { var resErr libkb.ResolutionError if errors.As(err, &resErr) { switch resErr.Kind { case libkb.ResolutionErrorRateLimited, libkb.ResolutionErrorInvalidInput, libkb.ResolutionErrorRequestFailed: return true } } return false } // Try to resolve implicit team members. // Modifies the arguments `resSet` and appends to `resolvedAssertions`. // For each assertion in `sourceAssertions`, try to resolve them. // // If they resolve, add the username to `resSet` and the assertion to `resolvedAssertions`. // If they don't resolve, add the SocialAssertion to `resSet`, but nothing to `resolvedAssertions`. func ResolveImplicitTeamSetUntrusted(ctx context.Context, g *libkb.GlobalContext, sourceAssertions []libkb.AssertionExpression, resSet *keybase1.ImplicitTeamUserSet, resolvedAssertions *[]libkb.ResolvedAssertion, ) error { m := libkb.NewMetaContext(ctx, g) for _, expr := range sourceAssertions { u, resolveRes, err := g.Resolver.ResolveUser(m, expr.String()) if err != nil { // Resolution failed. Could still be an SBS assertion. if shouldPreventTeamCreation(err) { // but if we are not sure, better to bail out return err } sa, err := expr.ToSocialAssertion() if err != nil { // Could not convert to a social assertion. // This could be because it is a compound assertion, which we do not support when SBS. // Or it could be because it's a team assertion or something weird like that. return libkb.ResolutionError{ Input: expr.String(), Msg: "unknown user assertion", Kind: libkb.ResolutionErrorNotFound, } } resSet.UnresolvedUsers = append(resSet.UnresolvedUsers, sa) } else { // Resolution succeeded resSet.KeybaseUsers = append(resSet.KeybaseUsers, u.Username) // Append the resolvee and assertion to resolvedAssertions, in case we identify later. *resolvedAssertions = append(*resolvedAssertions, libkb.ResolvedAssertion{ UID: u.Uid, Assertion: expr, ResolveResult: resolveRes, }) } } return nil } // Verify using Identify that a UID matches an assertion. func verifyResolveResult(ctx context.Context, g *libkb.GlobalContext, resolvedAssertion libkb.ResolvedAssertion) (err error) { defer g.CTrace(ctx, fmt.Sprintf("verifyResolveResult ID user [%s] %s", resolvedAssertion.UID, resolvedAssertion.Assertion.String()), &err)() if resolvedAssertion.ResolveResult.WasKBAssertion() { // The resolver does not use server-trust for these sorts of assertions. // So early out to avoid the performance cost of a full identify. return nil } if resolvedAssertion.ResolveResult.IsServerTrust() { g.Log.CDebugf(ctx, "Trusting the server on assertion: %q (server trust - no way for clients to verify)", resolvedAssertion.Assertion.String()) return nil } id2arg := keybase1.Identify2Arg{ Uid: resolvedAssertion.UID, UserAssertion: resolvedAssertion.Assertion.String(), CanSuppressUI: true, // Use CHAT_GUI to avoid tracker popups and DO externals checks. IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_GUI, } uis := libkb.UIs{ // Send a nil IdentifyUI, this IdentifyBehavior should not use it anyway. IdentifyUI: nil, } eng := engine.NewIdentify2WithUID(g, &id2arg) m := libkb.NewMetaContext(ctx, g).WithUIs(uis) err = engine.RunEngine2(m, eng) if err != nil { idRes, _ := eng.Result(m) m.Debug("identify failed (IDres %v, TrackBreaks %v): %v", idRes != nil, idRes != nil && idRes.TrackBreaks != nil, err) } return err } // Remove duplicates from a team display name. // Does not do any resolution nor resolution of UIDs. // "alice" -> "alice" // "alice,bob,alice" -> "alice" // "alice#alice" -> "alice" // "alice,bob#alice#char" -> "alice,bob#char" func deduplicateImplicitTeamDisplayName(name *keybase1.ImplicitTeamDisplayName) { seen := make(map[string]bool) unseen := func(idx string) bool { seenBefore := seen[idx] seen[idx] = true return !seenBefore } var writers keybase1.ImplicitTeamUserSet var readers keybase1.ImplicitTeamUserSet for _, u := range name.Writers.KeybaseUsers { if unseen(u) { writers.KeybaseUsers = append(writers.KeybaseUsers, u) } } for _, u := range name.Writers.UnresolvedUsers { if unseen(u.String()) { writers.UnresolvedUsers = append(writers.UnresolvedUsers, u) } } for _, u := range name.Readers.KeybaseUsers { if unseen(u) { readers.KeybaseUsers = append(readers.KeybaseUsers, u) } } for _, u := range name.Readers.UnresolvedUsers { if unseen(u.String()) { readers.UnresolvedUsers = append(readers.UnresolvedUsers, u) } } name.Writers = writers name.Readers = readers }