/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/libkb/loaduser.go
686 строк
18 KB
zoom-ua
remove golang.org/x/net/context in favor of context (#28708)
17 дек 2025, 18:24
Не верифицирован
17 дек 2025, 18:24
67b0ffe
Код
Авторство
О чём код?
// Copyright 2015 Keybase, Inc. All rights reserved. Use of // this source code is governed by the included BSD license. package libkb import ( "context" "fmt" "runtime/debug" "time" "github.com/keybase/client/go/jsonhelpers" keybase1 "github.com/keybase/client/go/protocol/keybase1" jsonw "github.com/keybase/go-jsonw" ) type UIDer interface { GetUID() keybase1.UID } type StubMode int const ( StubModeStubbed StubMode = 0 StubModeUnstubbed StubMode = 1 ) func StubModeFromUnstubbedBool(unstubbed bool) StubMode { if unstubbed { return StubModeUnstubbed } return StubModeStubbed } func (s StubMode) String() string { if s == StubModeUnstubbed { return "unstubbed" } return "stubbed" } type LoadUserArg struct { uid keybase1.UID name string // Can also be an assertion like foo@twitter publicKeyOptional bool noCacheResult bool // currently ignore self bool forceReload bool // ignore the cache entirely, don't even bother polling if it is out of date; just fetch new data. forcePoll bool // for cached user load, force a repoll. If this and StaleOK are both set, we try a network call and if it fails return the cached data. staleOK bool // if stale cached versions are OK (for immutable fields) cachedOnly bool // only return cached data (staleOK should be true and forcePoll must be false) uider UIDer abortIfSigchainUnchanged bool resolveBody *jsonw.Wrapper // some load paths plumb this through upakLite bool stubMode StubMode // by default, this is StubModeStubbed, meaning, stubbed links are OK forceMerkleServerPolling bool // can be used to force or suppress server merkle polling, if set // NOTE: We used to have these feature flags, but we got rid of them, to // avoid problems where a yes-features load doesn't accidentally get served // the result of an earlier no-features load from cache. We shouldn't add // any more flags like this unless we also add machinery to avoid that // mistake. // AllKeys bool // AllSubchains bool // We might have already loaded these if we're falling back from a // failed LoadUserPlusKeys load merkleLeaf *MerkleUserLeaf sigHints *SigHints m MetaContext } func (arg LoadUserArg) String() string { leaf := "nil" if arg.merkleLeaf != nil { leaf = fmt.Sprintf("%v", arg.merkleLeaf.idVersion) } return fmt.Sprintf("{UID:%s Name:%q PublicKeyOptional:%v NoCacheResult:%v Self:%v ForceReload:%v ForcePoll:%v StaleOK:%v AbortIfSigchainUnchanged:%v CachedOnly:%v Leaf:%v %v}", arg.uid, arg.name, arg.publicKeyOptional, arg.noCacheResult, arg.self, arg.forceReload, arg.forcePoll, arg.staleOK, arg.abortIfSigchainUnchanged, arg.cachedOnly, leaf, arg.stubMode) } func (arg LoadUserArg) MetaContext() MetaContext { return arg.m } func NewLoadUserArg(g *GlobalContext) LoadUserArg { return LoadUserArg{m: NewMetaContextBackground(g)} } func NewLoadUserArgWithMetaContext(m MetaContext) LoadUserArg { return LoadUserArg{ m: m, uider: m.LoginContext(), } } func NewLoadUserArgWithContext(ctx context.Context, g *GlobalContext) LoadUserArg { return LoadUserArg{ m: NewMetaContext(ctx, g), } } func NewLoadUserSelfArg(g *GlobalContext) LoadUserArg { ret := NewLoadUserArg(g) ret.self = true return ret } func NewLoadUserSelfAndUIDArg(g *GlobalContext) LoadUserArg { ret := NewLoadUserArg(g) ret.self = true ret.uid = g.GetMyUID() return ret } func NewLoadUserForceArg(g *GlobalContext) LoadUserArg { arg := NewLoadUserPubOptionalArg(g) arg.forceReload = true return arg } func NewLoadUserByNameArg(g *GlobalContext, name string) LoadUserArg { arg := NewLoadUserArg(g) arg.name = name return arg } func NewLoadUserByUIDArg(ctx context.Context, g *GlobalContext, uid keybase1.UID) LoadUserArg { arg := NewLoadUserArgWithMetaContext(NewMetaContext(ctx, g)) arg.uid = uid return arg } func NewLoadUserByUIDForceArg(g *GlobalContext, uid keybase1.UID) LoadUserArg { arg := NewLoadUserArg(g) arg.uid = uid arg.forceReload = true return arg } func NewLoadUserPubOptionalArg(g *GlobalContext) LoadUserArg { arg := NewLoadUserArg(g) arg.publicKeyOptional = true return arg } func (arg LoadUserArg) WithSelf(self bool) LoadUserArg { arg.self = self return arg } func (arg LoadUserArg) EnsureCtxAndLogTag() LoadUserArg { arg.m = arg.m.EnsureCtx().WithLogTag("LU") return arg } func (arg LoadUserArg) WithCachedOnly(b bool) LoadUserArg { arg.cachedOnly = b return arg } func (arg LoadUserArg) WithResolveBody(r *jsonw.Wrapper) LoadUserArg { arg.resolveBody = r return arg } func (arg LoadUserArg) WithName(n string) LoadUserArg { arg.name = n return arg } func (arg LoadUserArg) WithForceMerkleServerPolling(b bool) LoadUserArg { arg.forceMerkleServerPolling = b return arg } func (arg LoadUserArg) WithNetContext(ctx context.Context) LoadUserArg { arg.m = arg.m.WithCtx(ctx) return arg } func (arg LoadUserArg) WithUID(uid keybase1.UID) LoadUserArg { arg.uid = uid return arg } func (arg LoadUserArg) WithPublicKeyOptional() LoadUserArg { arg.publicKeyOptional = true return arg } func (arg LoadUserArg) ForUPAKLite() LoadUserArg { arg.upakLite = true return arg } func (arg LoadUserArg) WithForcePoll(fp bool) LoadUserArg { arg.forcePoll = fp return arg } func (arg LoadUserArg) WithStaleOK(b bool) LoadUserArg { arg.staleOK = b return arg } func (arg LoadUserArg) GetNetContext() context.Context { if ctx := arg.m.Ctx(); ctx != nil { return ctx } return context.Background() } func (arg LoadUserArg) WithLoginContext(l LoginContext) LoadUserArg { arg.uider = l return arg } func (arg LoadUserArg) WithAbortIfSigchainUnchanged() LoadUserArg { arg.abortIfSigchainUnchanged = true return arg } func (arg LoadUserArg) WithForceReload() LoadUserArg { arg.forceReload = true return arg } func (arg LoadUserArg) WithStubMode(sm StubMode) LoadUserArg { arg.stubMode = sm return arg } func (arg LoadUserArg) ToMerkleOpts() MerkleOpts { ret := MerkleOpts{} if !arg.forceReload && !arg.forcePoll && !arg.forceMerkleServerPolling { ret.NoServerPolling = true } return ret } func (arg *LoadUserArg) checkUIDName() error { if arg.uid.Exists() { return nil } if len(arg.name) == 0 && !arg.self { return fmt.Errorf("no username given to LoadUser") } if len(arg.name) > 0 && arg.self { return fmt.Errorf("If loading self, can't provide a username") } if !arg.self { return nil } if arg.uid = myUID(arg.m.G(), arg.uider); arg.uid.IsNil() { arg.name = arg.m.G().Env.GetUsername().String() if len(arg.name) == 0 { return SelfNotFoundError{msg: "could not find UID or username for self"} } } return nil } func (arg *LoadUserArg) resolveUID() (ResolveResult, error) { var rres ResolveResult if arg.uid.Exists() { return rres, nil } if len(arg.name) == 0 { // this won't happen anymore because check moved to // checkUIDName() func, but just in case return rres, fmt.Errorf("resolveUID: no uid or name") } if rres = arg.m.G().Resolver.ResolveWithBody(arg.m, arg.name).FailOnDeleted(); rres.err != nil { return rres, rres.err } if rres.uid.IsNil() { return rres, fmt.Errorf("No resolution for name=%s", arg.name) } arg.uid = rres.uid return rres, nil } // after resolution, check if this is a self load func (arg *LoadUserArg) checkSelf() { if arg.self { return } myuid := myUID(arg.m.G(), arg.uider) if myuid.Exists() && arg.uid.Exists() && myuid.Equal(arg.uid) { arg.self = true } } func LoadMe(arg LoadUserArg) (*User, error) { arg.self = true return LoadUser(arg) } func LoadMeByUID(ctx context.Context, g *GlobalContext, uid keybase1.UID) (*User, error) { return LoadMe(NewLoadUserByUIDArg(ctx, g, uid)) } func LoadMeByMetaContextAndUID(m MetaContext, uid keybase1.UID) (*User, error) { return LoadMe(NewLoadUserArgWithMetaContext(m).WithUID(uid)) } func LoadUser(arg LoadUserArg) (ret *User, err error) { m := arg.MetaContext().WithLogTag("LU") defer m.Trace(fmt.Sprintf("LoadUser(%s)", arg), &err)() var refresh bool if m.G().VDL.DumpSiteLoadUser() { debug.PrintStack() } // Whatever the reply is, pass along our desired global context var refreshReason string defer func() { if ret != nil { ret.SetGlobalContext(m.G()) if refresh { m.G().NotifyRouter.HandleUserChanged(m, ret.GetUID(), fmt.Sprintf("libkb.LoadUser refresh '%v'", refreshReason)) } } }() // make sure we have a uid or a name to load if err = arg.checkUIDName(); err != nil { return nil, err } m.Debug("LoadUser(uid=%v, name=%v)", arg.uid, arg.name) // resolve the uid from the name, if necessary rres, err := arg.resolveUID() if err != nil { return nil, err } // check to see if this is a self load arg.checkSelf() m.Debug("| resolved to %s", arg.uid) if arg.uid.Exists() { lock, err := m.G().loadUserLockTab.AcquireOnNameWithContextAndTimeout(m.Ctx(), m.G(), arg.uid.String(), 30*time.Second) if err != nil { m.Debug("| error acquiring singleflight lock for %s: %v", arg.uid, err) return nil, err } defer lock.Release(m.Ctx()) } // We can get the user object's body from either the resolution result or // if it was plumbed through as a parameter. resolveBody := rres.body if resolveBody == nil { resolveBody = arg.resolveBody } // get sig hints from local db in order to populate during merkle leaf lookup // They might have already been loaded in. var sigHints *SigHints if sigHints = arg.sigHints; sigHints == nil { sigHints, err = LoadSigHints(m, arg.uid) if err != nil { return nil, err } } // load user from local, remote ret, refresh, refreshReason, err = loadUser(m, arg.uid, resolveBody, sigHints, arg.forceReload, arg.merkleLeaf, arg.WithForceMerkleServerPolling(true).ToMerkleOpts()) if err != nil { return nil, err } ret.sigHints = sigHints // Match the returned User object to the Merkle tree. Also make sure // that the username queried for matches the User returned (if it // was indeed queried for) if err = ret.leaf.MatchUser(ret, arg.uid, rres.GetNormalizedQueriedUsername()); err != nil { return ret, err } if err = ret.LoadSigChains(m, &ret.leaf, arg.self, arg.stubMode); err != nil { return ret, err } if arg.abortIfSigchainUnchanged && ret.sigChain().wasFullyCached { return nil, nil } if ret.sigHints != nil && ret.sigHints.dirty { refresh = true } // Proactively cache fetches from remote server to local storage if e2 := ret.Store(m); e2 != nil { m.Warning("Problem storing user %s: %s", ret.GetName(), e2) } if ret.HasActiveKey() { if err = ret.MakeIDTable(m); err != nil { return ret, err } // Check that the user has self-signed only after we // consider revocations. See: https://github.com/keybase/go/issues/43 if err = ret.VerifySelfSig(); err != nil { return ret, err } cacheUserServiceSummary(m, ret) } else if !arg.publicKeyOptional { m.Debug("No active key for user: %s", ret.GetUID()) return ret, NoKeyError{} } return ret, err } func loadUser(m MetaContext, uid keybase1.UID, resolveBody *jsonw.Wrapper, sigHints *SigHints, force bool, leaf *MerkleUserLeaf, merkleOpts MerkleOpts) (*User, bool, string, error) { local, err := LoadUserFromLocalStorage(m, uid) var refresh bool var refreshReason string if err != nil { m.Warning("Failed to load %s from storage: %s", uid, err) } if leaf == nil { leaf, err = lookupMerkleLeaf(m, uid, (local != nil), sigHints, merkleOpts) if err != nil { return nil, refresh, refreshReason, err } } var f1, loadRemote bool if local == nil { m.Debug("| No local user stored for %s", uid) loadRemote = true } else if f1, refreshReason, err = local.CheckBasicsFreshness(leaf.idVersion); err != nil { return nil, refresh, refreshReason, err } else { loadRemote = !f1 refresh = loadRemote } m.Debug("| Freshness: basics=%v; for %s", f1, uid) var ret *User if !loadRemote && !force { ret = local } else if ret, err = LoadUserFromServer(m, uid, resolveBody); err != nil { return nil, refresh, refreshReason, err } if ret == nil { return nil, refresh, refreshReason, nil } if leaf != nil { ret.leaf = *leaf } return ret, refresh, refreshReason, nil } func LoadUserFromLocalStorage(m MetaContext, uid keybase1.UID) (u *User, err error) { m.Debug("+ LoadUserFromLocalStorage(%s)", uid) jw, err := m.G().LocalDb.Get(DbKeyUID(DBUser, uid)) if err != nil { return nil, err } if jw == nil { m.Debug("- loadUserFromLocalStorage(%s): Not found", uid) return nil, nil } m.Debug("| Loaded successfully") if u, err = NewUserFromLocalStorage(m.G(), jw); err != nil { return nil, err } if u.id.NotEqual(uid) { err = fmt.Errorf("Bad lookup; uid mismatch: %s != %s", uid, u.id) } m.Debug("| Loaded username %s (uid=%s)", u.name, uid) m.Debug("- LoadUserFromLocalStorage(%s,%s)", u.name, uid) return } // LoadUserEmails returns emails for logged in user func LoadUserEmails(m MetaContext) (emails []keybase1.Email, err error) { uid := m.G().GetMyUID() res, err := m.G().API.Get(m, APIArg{ Endpoint: "user/private", SessionType: APISessionTypeREQUIRED, Args: HTTPArgs{ "uid": UIDArg(uid), }, }) if err != nil { return } emailPayloads, err := jsonhelpers.JSONGetChildren(res.Body.AtKey("them").AtKey("emails").AtKey("emails")) if err != nil { return nil, err } for _, emailPayload := range emailPayloads { email, err := emailPayload.AtKey("email").GetString() if err != nil { return nil, err } isPrimary, err := emailPayload.AtKey("is_primary").GetInt() if err != nil { return nil, err } isVerified, err := emailPayload.AtKey("is_verified").GetInt() if err != nil { return nil, err } visibilityCode, err := emailPayload.AtKey("visibility").GetInt() if err != nil { return nil, err } var lastVerifyEmailDate int64 lastVerifyEmailDateNode := emailPayload.AtKey("last_verify_email_date") if lastVerifyEmailDateNode.IsOk() && !lastVerifyEmailDateNode.IsNil() { lastVerifyEmailDate, err = lastVerifyEmailDateNode.GetInt64() if err != nil { return nil, err } } emails = append(emails, keybase1.Email{ Email: keybase1.EmailAddress(email), IsVerified: isVerified == 1, IsPrimary: isPrimary == 1, Visibility: keybase1.IdentityVisibility(visibilityCode), LastVerifyEmailDate: keybase1.UnixTime(lastVerifyEmailDate), }) } return } func LoadUserFromServer(m MetaContext, uid keybase1.UID, body *jsonw.Wrapper) (u *User, err error) { m.Debug("Load User from server: %s", uid) // Res.body might already have been preloaded as a result of a Resolve call earlier. if body == nil { res, err := m.G().API.Get(m, APIArg{ Endpoint: "user/lookup", SessionType: APISessionTypeNONE, Args: HTTPArgs{ "uid": UIDArg(uid), "load_deleted": B{true}, }, }) if err != nil { return nil, err } body = res.Body.AtKey("them") } else { m.Debug("| Skipped load; got user object previously") } if u, err = NewUserFromServer(m.G(), body); err != nil { return u, err } m.Debug("- Load user from server: %s -> %s", uid, ErrToOk(err)) return u, err } func myUID(g *GlobalContext, uider UIDer) keybase1.UID { if uider != nil { return uider.GetUID() } return g.GetMyUID() } func lookupMerkleLeaf(m MetaContext, uid keybase1.UID, localExists bool, sigHints *SigHints, merkleOpts MerkleOpts) (f *MerkleUserLeaf, err error) { if uid.IsNil() { err = fmt.Errorf("uid parameter for lookupMerkleLeaf empty") return } q := NewHTTPArgs() q.Add("uid", UIDArg(uid)) f, err = m.G().MerkleClient.LookupUser(m, q, sigHints, merkleOpts) if err == nil && f == nil && localExists { err = fmt.Errorf("User not found in server Merkle tree") } return } func lookupSigHintsAndMerkleLeaf(m MetaContext, uid keybase1.UID, localExists bool, merkleOpts MerkleOpts) (sigHints *SigHints, leaf *MerkleUserLeaf, err error) { defer m.Trace("lookupSigHintsAndMerkleLeaf", &err)() sigHints, err = LoadSigHints(m, uid) if err != nil { return nil, nil, err } leaf, err = lookupMerkleLeaf(m, uid, true, sigHints, merkleOpts) if err != nil { return nil, nil, err } return sigHints, leaf, nil } // LoadUserPlusKeys loads user and keys for the given UID. If `pollForKID` is provided, we'll request // this user potentially twice: the first time can hit the cache for the UID, but will force a repoll // unless the pollForKID is found for the user. If pollForKID is empty, then just access the cache as // normal. func LoadUserPlusKeys(ctx context.Context, g *GlobalContext, uid keybase1.UID, pollForKID keybase1.KID) (keybase1.UserPlusKeys, error) { return g.GetUPAKLoader().LoadUserPlusKeys(ctx, uid, pollForKID) } // IsUserByUsernameOffline checks to see if the given username is a legit Keybase username, // using only our offline cache and materials. Useful if you don't mean to share info // with the server, as in chat @-mentions. Will return true if it's known to be a legit // user, and false if it can't say for sure. "Legit" users in this context might // be deleted or reset; they just once existing as a user. func IsUserByUsernameOffline(m MetaContext, un NormalizedUsername) bool { if m.G().UIDMapper.MapHardcodedUsernameToUID(un).Exists() { return true } // We already took care of the bad username casing in the harcoded exception list above, // so it's ok to treat the NormalizedUsername as a cased string. uid := usernameToUIDPreserveCase(un.String()) // use the UPAKLoader with StaleOK, CachedOnly in order to get cached upak arg := NewLoadUserArgWithMetaContext(m).WithUID(uid).WithPublicKeyOptional().WithStaleOK(true).WithCachedOnly(true) _, _, err := m.G().GetUPAKLoader().LoadV2(arg) if err == nil { return true } if _, ok := err.(UserNotFoundError); !ok { m.Debug("IsUserByUsernameOffline(%s) squashing error: %s", un.String(), err) } return false } func cacheUserServiceSummary(mctx MetaContext, user *User) { serviceMapper := mctx.G().ServiceMapper if serviceMapper == nil { // no service summary mapper in current context - e.g. in tests. return } remoteProofs := user.idTable.remoteProofLinks if remoteProofs != nil { summary := remoteProofs.toServiceSummary() err := serviceMapper.InformOfServiceSummary(mctx.Ctx(), mctx.G(), user.id, summary) if err != nil { mctx.Debug("cacheUserServiceSummary for %q uid: %q: error: %s", user.name, user.id, err) } } }