/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/libkb/context.go
715 строк
19 KB
chrisnojima-zoom
Version 670 - clean2 (#29122)
08 июн 2026, 19:31
Не верифицирован
08 июн 2026, 19:31
1943197
Код
Авторство
О чём код?
package libkb import ( "context" "fmt" "runtime/debug" "time" logger "github.com/keybase/client/go/logger" "github.com/keybase/client/go/profiling" keybase1 "github.com/keybase/client/go/protocol/keybase1" ) type APITokener interface { Tokens() (session, csrf string) } type MetaContext struct { ctx context.Context g *GlobalContext loginContext LoginContext activeDevice *ActiveDevice apiTokener APITokener uis UIs } func (m MetaContext) Dump() { m.Debug("MetaContext#Dump:") if m.activeDevice != nil { m.Debug("- Local ActiveDevice:") m.activeDevice.Dump(m, "-- ") } m.Debug("- Global ActiveDevice:") m.g.ActiveDevice.Dump(m, "-- ") if m.loginContext != nil { m.Debug("- Login Context:") m.loginContext.Dump(m, "-- ") } } func NewMetaContext(ctx context.Context, g *GlobalContext) MetaContext { return MetaContext{ctx: ctx, g: g} } func (m MetaContext) WithLoginContext(l LoginContext) MetaContext { m.loginContext = l return m } func (m MetaContext) WithAPITokener(t APITokener) MetaContext { m.apiTokener = t return m } func (m MetaContext) WithCtx(c context.Context) MetaContext { m.ctx = c return m } func (m MetaContext) G() *GlobalContext { return m.g } func (m MetaContext) Ctx() context.Context { return m.ctx } func (m MetaContext) LoginContext() LoginContext { return m.loginContext } func (m MetaContext) VLogf(lev VDebugLevel, msg string, args ...any) { m.g.VDL.CLogfWithAddedDepth(m.ctx, lev, 1, msg, args...) } func (m MetaContext) Trace(msg string, err *error) func() { return CTrace(m.ctx, m.g.Log.CloneWithAddedDepth(1), msg, err, m.G().Clock()) } func (m MetaContext) VTrace(lev VDebugLevel, msg string, err *error) func() { return m.g.CVTrace(m.ctx, lev, msg, err) } func (m MetaContext) PerfTrace(msg string, err *error) func() { return CTrace(m.ctx, m.g.PerfLog.CloneWithAddedDepth(1), msg, err, m.G().Clock()) } func (m MetaContext) TimeBuckets() (MetaContext, *profiling.TimeBuckets) { var ret *profiling.TimeBuckets m.ctx, ret = m.G().CTimeBuckets(m.ctx) return m, ret } func (m MetaContext) TimeTracer(label string, enabled bool) profiling.TimeTracer { return m.G().CTimeTracer(m.Ctx(), label, enabled) } func (m MetaContext) Debug(f string, args ...any) { m.g.Log.CloneWithAddedDepth(1).CDebugf(m.ctx, f, args...) } func (m MetaContext) PerfDebug(f string, args ...any) { m.g.PerfLog.CloneWithAddedDepth(1).CDebugf(m.ctx, f, args...) } func (m MetaContext) Warning(f string, args ...any) { m.g.Log.CloneWithAddedDepth(1).CWarningf(m.ctx, f, args...) } func (m MetaContext) Error(f string, args ...any) { m.g.Log.CloneWithAddedDepth(1).CErrorf(m.ctx, f, args...) } func (m MetaContext) Info(f string, args ...any) { m.g.Log.CloneWithAddedDepth(1).CInfof(m.ctx, f, args...) } func (m MetaContext) ActiveDevice() *ActiveDevice { if m.activeDevice != nil { m.Debug("MetaContext#ActiveDevice: thread local") return m.activeDevice } m.Debug("MetaContext#ActiveDevice: global") return m.G().ActiveDevice } func (m MetaContext) NIST() (*NIST, error) { nist, uid, _, err := m.ActiveDevice().NISTAndUIDDeviceID(m.Ctx()) if err != nil { return nil, err } if !uid.Equal(m.CurrentUID()) { m.Debug("MetaContext#NIST: Not returning nist, since for wrong UID: %s != %s", uid, m.CurrentUID()) return nil, nil } return nist, nil } func NewMetaContextTODO(g *GlobalContext) MetaContext { return MetaContext{ctx: context.TODO(), g: g} } func NewMetaContextBackground(g *GlobalContext) MetaContext { return MetaContext{ctx: context.Background(), g: g} } func (m MetaContext) WithDelegatedIdentifyUI(u IdentifyUI) MetaContext { m.uis.IdentifyUI = u return m } func (m MetaContext) WithContext(ctx context.Context) MetaContext { m.ctx = ctx return m } func (m MetaContext) WithContextCancel() (MetaContext, func()) { var f func() m.ctx, f = context.WithCancel(m.ctx) return m, f } func (m MetaContext) BackgroundWithCancel() (MetaContext, func()) { var f func() m.ctx, f = context.WithCancel(context.Background()) return m, f } func (m MetaContext) BackgroundWithLogTags() MetaContext { m.ctx = CopyTagsToBackground(m.ctx) return m } func (m MetaContext) WithTimeout(timeout time.Duration) (MetaContext, func()) { var f func() m.ctx, f = context.WithTimeout(m.ctx, timeout) return m, f } func (m MetaContext) WithLogTag(k string) MetaContext { m.ctx = WithLogTag(m.ctx, k) return m } func (m MetaContext) WithLogTags(tags map[string]string) MetaContext { for k, v := range tags { m.ctx = WithLogTagWithValue(m.ctx, k, v) } return m } func (m MetaContext) WithTimeBuckets() (MetaContext, *profiling.TimeBuckets) { ctx, tbs := m.G().CTimeBuckets(m.ctx) m.ctx = ctx return m, tbs } func (m MetaContext) EnsureCtx() MetaContext { if m.ctx == nil { m.ctx = context.Background() m.Debug("installing background context.Context") } return m } func (m MetaContext) WithSecretUI(u SecretUI) MetaContext { m.uis.SecretUI = u return m } func (m MetaContext) WithLogUI(u LogUI) MetaContext { m.uis.LogUI = u return m } func (m MetaContext) WithPgpUI(u PgpUI) MetaContext { m.uis.PgpUI = u return m } func (m MetaContext) WithIdentifyUI(u IdentifyUI) MetaContext { m.uis.IdentifyUI = u return m } func (m MetaContext) WithGPGUI(u GPGUI) MetaContext { m.uis.GPGUI = u return m } func (m MetaContext) WithSaltpackUI(s SaltpackUI) MetaContext { m.uis.SaltpackUI = s return m } func (m MetaContext) UIs() UIs { return m.uis } func (m MetaContext) WithUIs(u UIs) MetaContext { m.uis = u return m } func (m MetaContext) WithActiveDevice(a *ActiveDevice) MetaContext { m.activeDevice = a return m } func (m MetaContext) WithProvisioningKeyActiveDevice(d *DeviceWithKeys, uv keybase1.UserVersion) MetaContext { return m.WithActiveDevice(d.ToProvisioningKeyActiveDevice(m, uv)) } func (m MetaContext) WithGlobalActiveDevice() MetaContext { m.activeDevice = nil return m } func (m MetaContext) SecretKeyPromptArg(ska SecretKeyArg, reason string) SecretKeyPromptArg { return SecretKeyPromptArg{ SecretUI: m.uis.SecretUI, Ska: ska, Reason: reason, } } func (m MetaContext) WithNewProvisionalLoginContext() MetaContext { return m.WithLoginContext(newProvisionalLoginContext(m)) } func (m MetaContext) WithNewProvisionalLoginContextForUser(u *User) MetaContext { return m.WithNewProvisionalLoginContextForUserVersionAndUsername(u.ToUserVersion(), u.GetNormalizedName()) } func (m MetaContext) WithNewProvisionalLoginContextForUserVersionAndUsername(uv keybase1.UserVersion, un NormalizedUsername) MetaContext { plc := newProvisionalLoginContextWithUserVersionAndUsername(m, uv, un) err := m.ActiveDevice().CopyCacheToLoginContextIfForUserVersion(m, plc, uv) if err != nil { m.Debug("WithNewProvisionalLoginContextForUserVersionAndUsername: error %+v", err) } return m.WithLoginContext(plc) } func (m MetaContext) CommitProvisionalLogin() MetaContext { m.Debug("MetaContext#CommitProvisionalLogin") lctx := m.loginContext m.loginContext = nil if lctx != nil { if ppsc := lctx.PassphraseStreamCache(); ppsc != nil { m.ActiveDevice().CachePassphraseStream(ppsc) } } return m } type UIs struct { GPGUI GPGUI LogUI LogUI LoginUI LoginUI SecretUI SecretUI IdentifyUI IdentifyUI PgpUI PgpUI ProveUI ProveUI ProvisionUI ProvisionUI SaltpackUI SaltpackUI // Usually set to `NONE`, meaning none specified. // But if we know it, specify the end client type here // since some things like GPG shell-out work differently // depending. ClientType keybase1.ClientType SessionID int } func (e UIs) HasUI(kind UIKind) bool { switch kind { case GPGUIKind: return e.GPGUI != nil case LogUIKind: return e.LogUI != nil case LoginUIKind: return e.LoginUI != nil case SecretUIKind: return e.SecretUI != nil case IdentifyUIKind: return e.IdentifyUI != nil case PgpUIKind: return e.PgpUI != nil case ProveUIKind: return e.ProveUI != nil case ProvisionUIKind: return e.ProvisionUI != nil case SaltpackUIKind: return e.SaltpackUI != nil } panic(fmt.Sprintf("unhandled kind: %d", kind)) } type MetaContextified struct { m MetaContext } func (m MetaContextified) M() MetaContext { return m.m } func (m MetaContextified) G() *GlobalContext { return m.m.g } func NewMetaContextified(m MetaContext) MetaContextified { return MetaContextified{m: m} } // SwitchUserNewConfig switches the global active "user" as far as the global // config file is concerned. It switches the user to a new user, and therefore // you should specify the username, salt, and device ID for this user on this // device. It will take out the global `switchUserMu` and also clear out the // global ActiveDevice at the same time. We follow the same pattern here and // elsewhere: atomically mutate the `current_user` of the config file as we set // the global ActiveDevice. func (m MetaContext) SwitchUserNewConfig(u keybase1.UID, n NormalizedUsername, salt []byte, d keybase1.DeviceID) error { return m.switchUserNewConfig(u, n, salt, d, nil) } func (m MetaContext) switchUserNewConfig(u keybase1.UID, n NormalizedUsername, salt []byte, d keybase1.DeviceID, ad *ActiveDevice) error { g := m.G() defer g.switchUserMu.Acquire(m, "switchUserNewConfig")() cw := g.Env.GetConfigWriter() if cw == nil { return NoConfigWriterError{} } // Note that `true` here means that an existing user config entry will // be overwritten. if err := cw.SetUserConfig(NewUserConfig(u, n, salt, d), true /* overwrite */); err != nil { return err } // Clear stayLoggedOut, so that if the service restarts for any reason // we will know that we are logged in. if g.Env.GetStayLoggedOut() { if err := cw.SetStayLoggedOut(false); err != nil { return err } } return g.ActiveDevice.SetOrClear(m, ad) } // SwitchUserNewConfigActiveDevice creates a new config file stanza and an // active device for the given user, all while holding the switchUserMu lock. func (m MetaContext) SwitchUserNewConfigActiveDevice(uv keybase1.UserVersion, n NormalizedUsername, salt []byte, d keybase1.DeviceID, sigKey GenericKey, encKey GenericKey, deviceName string, keychainMode KeychainMode) error { ad := NewProvisionalActiveDevice(m, uv, d, sigKey, encKey, deviceName, keychainMode) return m.switchUserNewConfig(uv.Uid, n, salt, d, ad) } // SwitchUserNukeConfig removes the given username from the config file, and // then switches to not having a current user (by clearing the ActiveDevice, // etc). It does this in a critical section, holding switchUserMu. func (m MetaContext) SwitchUserNukeConfig(n NormalizedUsername) error { g := m.G() defer g.switchUserMu.Acquire(m, "SwitchUserNukeConfig")() cw := g.Env.GetConfigWriter() cr := g.Env.GetConfig() if cw == nil { return NoConfigWriterError{} } if cr == nil { return NoConfigFileError{} } uid := cr.GetUIDForUsername(n) err := cw.NukeUser(n) if err != nil { return err } if g.ActiveDevice.UID().Equal(uid) { err := g.ActiveDevice.Clear() if err != nil { return err } } return nil } func (m MetaContext) SwitchUser(n NormalizedUsername) error { return m.SwitchUserToActiveDevice(n, nil) } func (m MetaContext) SwitchUserToActiveDevice(n NormalizedUsername, ad *ActiveDevice) (err error) { defer m.Trace(fmt.Sprintf("MetaContext#SwitchUserToActiveDevice(%s,ActiveDevice:%v)", n.String(), (ad != nil)), &err)() g := m.G() if n.IsNil() { return nil } if !n.IsValid() { return NewBadUsernameError(n.String()) } defer g.switchUserMu.Acquire(m, "SwitchUserToActiveDevice %v", n)() cw := g.Env.GetConfigWriter() if cw == nil { return NoConfigWriterError{} } err = cw.SwitchUser(n) if _, ok := err.(UserNotFoundError); ok { m.Debug("| No user %s found; clearing out config", n) err = nil } if err != nil { return err } err = g.ActiveDevice.SetOrClear(m, ad) if err != nil { return err } m.CommitProvisionalLogin() return nil } func (m MetaContext) SwitchUserDeprovisionNukeConfig(username NormalizedUsername) error { g := m.G() defer g.switchUserMu.Acquire(m, "SwitchUserDeprovisionNukeConfig %v", username)() cw := g.Env.GetConfigWriter() if cw == nil { return NoConfigWriterError{} } if err := cw.NukeUser(username); err != nil { return err } // The config entries we just nuked could still be in memory. Clear them. return cw.SetUserConfig(nil, true /* overwrite; ignored */) } // SetActiveOneshotDevice acquires the switchUserMu mutex, setting the active // device to one that corresponds to the given UID and DeviceWithKeys, and also // sets the config file to a temporary in-memory config (not writing to disk) // to satisfy local requests for g.Env.* func (m MetaContext) SwitchUserToActiveOneshotDevice(uv keybase1.UserVersion, nun NormalizedUsername, d *DeviceWithKeys) (err error) { defer m.Trace("MetaContext#SwitchUserToActiveOneshotDevice", &err)() g := m.G() defer g.switchUserMu.Acquire(m, "SwitchUserToActiveOneshotDevice")() cw := g.Env.GetConfigWriter() if cw == nil { return NoConfigWriterError{} } ad := d.ToProvisioningKeyActiveDevice(m, uv) err = g.ActiveDevice.Copy(m, ad) if err != nil { return err } uc := NewOneshotUserConfig(uv.Uid, nun, nil, d.DeviceID()) err = cw.SetUserConfig(uc, false) if err != nil { return err } return nil } // SwitchUserLoggedOut clears the active device and the current_user stanza of // the config file, all while holding the switchUserMu func (m MetaContext) SwitchUserLoggedOut() (err error) { defer m.Trace("MetaContext#SwitchUserLoggedOut", &err)() g := m.G() defer g.switchUserMu.Acquire(m, "SwitchUserLoggedOut")() cw := g.Env.GetConfigWriter() if cw == nil { return NoConfigWriterError{} } err = g.ActiveDevice.Clear() if err != nil { return err } err = cw.SetUserConfig(nil, false) if err != nil { return err } return nil } // SetActiveDevice sets the active device to have the UserVersion, deviceID, // sigKey, encKey and deviceName as specified, and does so while grabbing the // global switchUser lock, since it should be sycnhronized with attempts to // switch the global logged in user. It does not, however, change the // `current_user` in the config file, or edit the global config file in any // way. func (m MetaContext) SetActiveDevice(uv keybase1.UserVersion, deviceID keybase1.DeviceID, sigKey, encKey GenericKey, deviceName string, keychainMode KeychainMode, ) error { g := m.G() defer g.switchUserMu.Acquire(m, "SetActiveDevice")() if !g.Env.GetUID().Equal(uv.Uid) { return NewUIDMismatchError("UID switched out from underneath provisioning process") } return g.ActiveDevice.Set(m, uv, deviceID, sigKey, encKey, deviceName, 0, keychainMode) } func (m MetaContext) SetSigningKey(uv keybase1.UserVersion, deviceID keybase1.DeviceID, sigKey GenericKey, deviceName string) error { g := m.G() defer g.switchUserMu.Acquire(m, "SetSigningKey")() return g.ActiveDevice.setSigningKey(g, uv, deviceID, sigKey, deviceName) } func (m MetaContext) SetEncryptionKey(uv keybase1.UserVersion, deviceID keybase1.DeviceID, encKey GenericKey) error { g := m.G() defer g.switchUserMu.Acquire(m, "SetEncryptionKey")() return g.ActiveDevice.setEncryptionKey(uv, deviceID, encKey) } // LogoutAndDeprovisionIfRevoked loads the user and checks if the current // device keys have been revoked. If so, it calls Logout and then runs the // ClearSecretsOnDeprovision func (m MetaContext) LogoutAndDeprovisionIfRevoked() (err error) { m = m.WithLogTag("LOIR") defer m.Trace("GlobalContext#LogoutAndDeprovisionIfRevoked", &err)() if !m.ActiveDevice().Valid() { m.Debug("LogoutAndDeprovisionIfRevoked: skipping check (not logged in)") return nil } if m.G().Env.GetSkipLogoutIfRevokedCheck() { m.Debug("LogoutAndDeprovisionIfRevoked: skipping check (SkipLogoutIfRevokedCheck)") return nil } doLogout := false err = CheckCurrentUIDDeviceID(m) switch err.(type) { case nil: m.Debug("LogoutAndDeprovisionIfRevoked: current device ok") case DeviceNotFoundError: m.Debug("LogoutAndDeprovisionIfRevoked: device not found error; user was likely reset; calling logout (%s)", err) doLogout = true case KeyRevokedError: m.Debug("LogoutAndDeprovisionIfRevoked: key revoked error error; device was revoked; calling logout (%s)", err) doLogout = true default: m.Debug("LogoutAndDeprovisionIfRevoked: non-actionable error: %s", err) } if doLogout { username := m.G().Env.GetUsername() if err := m.LogoutWithOptions(LogoutOptions{KeepSecrets: false, Force: true}); err != nil { return err } return ClearSecretsOnDeprovision(m, username) } return nil } func (m MetaContext) PassphraseStream() *PassphraseStream { if m.LoginContext() != nil { if m.LoginContext().PassphraseStreamCache() == nil { return nil } return m.LoginContext().PassphraseStreamCache().PassphraseStream() } return m.ActiveDevice().PassphraseStream() } func (m MetaContext) PassphraseStreamAndTriplesec() (*PassphraseStream, Triplesec) { var ppsc *PassphraseStreamCache if m.LoginContext() != nil { ppsc = m.LoginContext().PassphraseStreamCache() } else { ppsc = m.ActiveDevice().PassphraseStreamCache() } if ppsc == nil { return nil, nil } return ppsc.PassphraseStreamAndTriplesec() } func (m MetaContext) TriplesecAndGeneration() (ret Triplesec, ppgen PassphraseGeneration) { var pps *PassphraseStream pps, ret = m.PassphraseStreamAndTriplesec() if pps == nil { return nil, ppgen } ppgen = pps.Generation() if ppgen.IsNil() { return nil, ppgen } return ret, ppgen } func (m MetaContext) CurrentUsername() NormalizedUsername { if m.LoginContext() != nil { return m.LoginContext().GetUsername() } return m.ActiveDevice().Username(m) } func (m MetaContext) CurrentUID() keybase1.UID { if m.LoginContext() != nil { return m.LoginContext().GetUID() } return m.ActiveDevice().UID() } func (m MetaContext) CurrentUserVersion() keybase1.UserVersion { if m.LoginContext() != nil { return m.LoginContext().GetUserVersion() } return m.ActiveDevice().UserVersion() } func (m MetaContext) HasAnySession() (ret bool) { defer m.Trace("MetaContext#HasAnySession", nil)() if m.LoginContext() != nil { ok, _ := m.LoginContext().LoggedInLoad() if ok { m.Debug("| has temporary login session") return true } } if m.ActiveDevice().Valid() { m.Debug("| has valid device") return true } return false } func (m MetaContext) SyncSecrets() (ss *SecretSyncer, err error) { defer m.Trace("MetaContext#SyncSecrets", &err)() if m.LoginContext() != nil { err = m.LoginContext().RunSecretSyncer(m, keybase1.UID("")) if err != nil { return nil, err } return m.LoginContext().SecretSyncer(), nil } return m.ActiveDevice().SyncSecrets(m) } func (m MetaContext) SyncSecretsForUID(u keybase1.UID) (ss *SecretSyncer, err error) { defer m.Trace("MetaContext#SyncSecrets", &err)() return m.ActiveDevice().SyncSecretsForUID(m, u, false /* force */) } func (m MetaContext) ProvisionalSessionArgs() (token string, csrf string) { if m.LoginContext() == nil { return "", "" } sess := m.LoginContext().LocalSession() if sess == nil || !sess.IsValid() { return "", "" } return sess.token, sess.csrf } func (m MetaContext) Keyring() (ret *SKBKeyringFile, err error) { defer m.Trace("MetaContext#Keyring", &err)() if m.LoginContext() != nil { return m.LoginContext().Keyring(m) } return m.ActiveDevice().Keyring(m) } var _ logger.ContextInterface = MetaContext{} func (m MetaContext) UpdateContextToLoggerContext(c context.Context) logger.ContextInterface { return m.WithContext(c) } func (m MetaContext) DebugStack() { m.Debug("stack trace:\n%s", string(debug.Stack())) }