/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/engine/device_history.go
235 строк
6 KB
chrisnojima-zoom
Version 670 - clean2 (#29122)
08 июн 2026, 19:31
Не верифицирован
08 июн 2026, 19:31
1943197
Код
Авторство
О чём код?
// Copyright 2015 Keybase, Inc. All rights reserved. Use of // this source code is governed by the included BSD license. package engine import ( "errors" "fmt" "time" "github.com/keybase/client/go/kbcrypto" "github.com/keybase/client/go/libkb" keybase1 "github.com/keybase/client/go/protocol/keybase1" ) // DeviceHistory is an engine. type DeviceHistory struct { libkb.Contextified username string devices []keybase1.DeviceDetail } // NewDeviceHistory creates a DeviceHistory engine to lookup the // device history for username. func NewDeviceHistory(g *libkb.GlobalContext, username string) *DeviceHistory { return &DeviceHistory{ Contextified: libkb.NewContextified(g), username: username, } } // NewDeviceHistorySelf creates a DeviceHistory engine to lookup // the device history of the current user. func NewDeviceHistorySelf(g *libkb.GlobalContext) *DeviceHistory { return &DeviceHistory{ Contextified: libkb.NewContextified(g), } } // Name is the unique engine name. func (e *DeviceHistory) Name() string { return "DeviceHistory" } // GetPrereqs returns the engine prereqs. func (e *DeviceHistory) Prereqs() Prereqs { if len(e.username) > 0 { return Prereqs{} } return Prereqs{Device: true} } // RequiredUIs returns the required UIs. func (e *DeviceHistory) RequiredUIs() []libkb.UIKind { return []libkb.UIKind{} } // SubConsumers returns the other UI consumers for this engine. func (e *DeviceHistory) SubConsumers() []libkb.UIConsumer { return nil } // Run starts the engine. func (e *DeviceHistory) Run(m libkb.MetaContext) error { arg := e.loadUserArg(m) err := m.G().GetFullSelfer().WithUser(arg, func(u *libkb.User) error { return e.loadDevices(m, u) }) return err } func (e *DeviceHistory) Devices() []keybase1.DeviceDetail { return e.devices } func (e *DeviceHistory) loadUserArg(m libkb.MetaContext) libkb.LoadUserArg { arg := libkb.NewLoadUserPubOptionalArg(m.G()) if len(e.username) == 0 { arg = arg.WithSelf(true) } else { arg = arg.WithName(e.username) } return arg } func (e *DeviceHistory) loadDevices(m libkb.MetaContext, user *libkb.User) error { ckf := user.GetComputedKeyFamily() if ckf == nil { return errors.New("nil ComputedKeyFamily for user") } ckis := user.GetComputedKeyInfos() if ckis == nil { return errors.New("nil ComputedKeyInfos for user") } for _, d := range ckf.GetAllDevices() { exp := keybase1.DeviceDetail{Device: *(d.ProtExportWithDeviceNum())} cki, ok := ckis.Infos[d.Kid] if !ok { return fmt.Errorf("no ComputedKeyInfo for device %s, kid %s", d.ID, d.Kid) } if cki.Eldest { exp.Eldest = true } else { prov, err := e.provisioner(m, d, ckis, cki) if err != nil { return err } if prov != nil { exp.Provisioner = prov.ProtExport() t := keybase1.TimeFromSeconds(cki.DelegatedAt.Unix) exp.ProvisionedAt = &t } } if cki.RevokedAt != nil { rt := keybase1.TimeFromSeconds(cki.RevokedAt.Unix) exp.RevokedAt = &rt } if !cki.RevokedBy.IsNil() { exp.RevokedBy = cki.RevokedBy if deviceID, ok := ckis.KIDToDeviceID[cki.RevokedBy]; ok { if device, ok := ckis.Devices[deviceID]; ok { exp.RevokedByDevice = device.ProtExport() } } } if m.G().Env.GetDeviceIDForUsername(user.GetNormalizedName()).Eq(d.ID) { exp.CurrentDevice = true } e.devices = append(e.devices, exp) } // Load the last used times, but only if these are your own devices. The // API won't give you those times for other people's devices. if user.GetNormalizedName().Eq(m.G().Env.GetUsername()) { lastUsedTimes, err := e.getLastUsedTimes(m) if err != nil { return err } // If any active device is absent from the cache (e.g. a paper key // provisioned after the last secrets sync), force a network sync so // every device gets a real LastUsedTime on this first load. for _, detail := range e.devices { if detail.RevokedAt != nil { continue } if _, ok := lastUsedTimes[detail.Device.DeviceID]; !ok { lastUsedTimes, err = e.getLastUsedTimesForce(m) if err != nil { return err } break } } for i := range e.devices { detail := &e.devices[i] lastUsedTime, ok := lastUsedTimes[detail.Device.DeviceID] if !ok { continue } detail.Device.LastUsedTime = keybase1.TimeFromSeconds(lastUsedTime.Unix()) } } return nil } func (e *DeviceHistory) provisioner(m libkb.MetaContext, d libkb.DeviceWithDeviceNumber, ckis *libkb.ComputedKeyInfos, info *libkb.ComputedKeyInfo) (*libkb.Device, error) { for _, v := range info.Delegations { if kbcrypto.AlgoType(v.GetKeyType()) != kbcrypto.KIDNaclEddsa { // only concerned with device history, not pgp provisioners continue } did, ok := ckis.KIDToDeviceID[v] if !ok { return nil, fmt.Errorf("device %s provisioned by kid %s, but couldn't find matching device ID in ComputedKeyInfos", d.ID, v) } prov, ok := ckis.Devices[did] if !ok { return nil, fmt.Errorf("device %s provisioned by device %s, but couldn't find matching device in ComputedKeyInfos", d.ID, did) } return prov, nil } return nil, nil } func (e *DeviceHistory) getLastUsedTimes(m libkb.MetaContext) (ret map[keybase1.DeviceID]time.Time, err error) { defer m.Trace("DeviceHistory#getLastUsedTimes", &err)() var devs libkb.DeviceKeyMap var ss *libkb.SecretSyncer // Use local cache to avoid a blocking network call on every page load. // Falls back to a forced network sync when the cache is empty. ss, err = m.ActiveDevice().SyncSecretsFromCache(m) if err != nil { return nil, err } if !ss.HasDevices() { ss, err = m.ActiveDevice().SyncSecretsForce(m) if err != nil { return nil, err } } devs, err = ss.ActiveDevices(libkb.AllDeviceTypes) if err != nil { return nil, err } ret = map[keybase1.DeviceID]time.Time{} for deviceID, dev := range devs { ret[deviceID] = time.Unix(dev.LastUsedTime, 0) } return ret, nil } func (e *DeviceHistory) getLastUsedTimesForce(m libkb.MetaContext) (ret map[keybase1.DeviceID]time.Time, err error) { defer m.Trace("DeviceHistory#getLastUsedTimesForce", &err)() ss, err := m.ActiveDevice().SyncSecretsForce(m) if err != nil { return nil, err } devs, err := ss.ActiveDevices(libkb.AllDeviceTypes) if err != nil { return nil, err } ret = map[keybase1.DeviceID]time.Time{} for deviceID, dev := range devs { ret[deviceID] = time.Unix(dev.LastUsedTime, 0) } return ret, nil }