github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/sig_hints.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 libkb 5 6 import ( 7 "fmt" 8 9 keybase1 "github.com/keybase/client/go/protocol/keybase1" 10 jsonw "github.com/keybase/go-jsonw" 11 ) 12 13 type SigHint struct { 14 sigID keybase1.SigID 15 remoteID string 16 apiURL string 17 humanURL string 18 checkText string 19 // `isVerified` indicates if the client generated the values or they were 20 // received from the server and are trusted but not verified. 21 isVerified bool 22 } 23 24 func (sh SigHint) GetHumanURL() string { return sh.humanURL } 25 func (sh SigHint) GetAPIURL() string { return sh.apiURL } 26 func (sh SigHint) GetCheckText() string { return sh.checkText } 27 28 func NewSigHint(jw *jsonw.Wrapper) (sh *SigHint, err error) { 29 if jw == nil || !jw.IsOk() { 30 return nil, nil 31 } 32 sh = &SigHint{} 33 sh.sigID, err = GetSigID(jw.AtKey("sig_id")) 34 sh.remoteID, _ = jw.AtKey("remote_id").GetString() 35 sh.apiURL, _ = jw.AtKey("api_url").GetString() 36 sh.humanURL, _ = jw.AtKey("human_url").GetString() 37 sh.checkText, _ = jw.AtKey("proof_text_check").GetString() 38 sh.isVerified, _ = jw.AtKey("isVerified").GetBool() 39 return sh, err 40 } 41 42 func NewVerifiedSigHint(sigID keybase1.SigID, remoteID, apiURL, humanURL, checkText string) *SigHint { 43 return &SigHint{ 44 sigID: sigID, 45 remoteID: remoteID, 46 apiURL: apiURL, 47 humanURL: humanURL, 48 checkText: checkText, 49 isVerified: true, 50 } 51 } 52 53 func (sh SigHint) MarshalToJSON() *jsonw.Wrapper { 54 ret := jsonw.NewDictionary() 55 _ = ret.SetKey("sig_id", jsonw.NewString(sh.sigID.String())) 56 _ = ret.SetKey("remote_id", jsonw.NewString(sh.remoteID)) 57 _ = ret.SetKey("api_url", jsonw.NewString(sh.apiURL)) 58 _ = ret.SetKey("human_url", jsonw.NewString(sh.humanURL)) 59 _ = ret.SetKey("proof_text_check", jsonw.NewString(sh.checkText)) 60 _ = ret.SetKey("is_verified", jsonw.NewBool(sh.isVerified)) 61 return ret 62 } 63 64 type SigHints struct { 65 Contextified 66 uid keybase1.UID 67 version int 68 hints map[keybase1.SigIDMapKey]*SigHint 69 dirty bool 70 } 71 72 func NewSigHints(jw *jsonw.Wrapper, uid keybase1.UID, dirty bool, g *GlobalContext) (sh *SigHints, err error) { 73 sh = &SigHints{ 74 uid: uid, 75 dirty: dirty, 76 version: 0, 77 Contextified: NewContextified(g), 78 } 79 err = sh.PopulateWith(jw) 80 if err != nil { 81 sh = nil 82 } 83 return 84 } 85 86 func (sh SigHints) Lookup(i keybase1.SigID) *SigHint { 87 obj := sh.hints[i.ToMapKey()] 88 return obj 89 } 90 91 func (sh *SigHints) PopulateWith(jw *jsonw.Wrapper) (err error) { 92 93 if jw == nil || jw.IsNil() { 94 return 95 } 96 97 jw.AtKey("version").GetIntVoid(&sh.version, &err) 98 if err != nil { 99 return 100 } 101 102 sh.hints = make(map[keybase1.SigIDMapKey]*SigHint) 103 var n int 104 n, err = jw.AtKey("hints").Len() 105 if err != nil { 106 return 107 } 108 109 for i := 0; i < n; i++ { 110 hint, tmpe := NewSigHint(jw.AtKey("hints").AtIndex(i)) 111 if tmpe != nil { 112 sh.G().Log.Warning("Bad SigHint Loaded: %s", tmpe) 113 } else { 114 sh.hints[hint.sigID.ToMapKey()] = hint 115 } 116 } 117 return 118 } 119 120 func (sh SigHints) MarshalToJSON() *jsonw.Wrapper { 121 ret := jsonw.NewDictionary() 122 _ = ret.SetKey("version", jsonw.NewInt(sh.version)) 123 _ = ret.SetKey("hints", jsonw.NewArray(len(sh.hints))) 124 i := 0 125 for _, v := range sh.hints { 126 _ = ret.AtKey("hints").SetIndex(i, v.MarshalToJSON()) 127 i++ 128 } 129 return ret 130 } 131 132 func (sh *SigHints) Store(m MetaContext) (err error) { 133 m.Debug("+ SigHints.Store() for uid=%s", sh.uid) 134 if sh.dirty { 135 err = sh.G().LocalDb.Put(DbKeyUID(DBSigHints, sh.uid), []DbKey{}, sh.MarshalToJSON()) 136 sh.dirty = false 137 } else { 138 m.Debug("| SigHints.Store() skipped; wasn't dirty") 139 } 140 m.Debug("- SigHints.Store() for uid=%s -> %v", sh.uid, ErrToOk(err)) 141 return err 142 } 143 144 func LoadSigHints(m MetaContext, uid keybase1.UID) (sh *SigHints, err error) { 145 defer m.Trace(fmt.Sprintf("+ LoadSigHints(%s)", uid), &err)() 146 var jw *jsonw.Wrapper 147 jw, err = m.G().LocalDb.Get(DbKeyUID(DBSigHints, uid)) 148 if err != nil { 149 jw = nil 150 m.Debug("| SigHints failed to access local storage: %s", err) 151 } 152 // jw might be nil here, but that's allowed. 153 sh, err = NewSigHints(jw, uid, false, m.G()) 154 if err == nil { 155 m.Debug("| SigHints loaded @v%d", sh.version) 156 } 157 m.Debug("- LoadSigHints(%s)", uid) 158 return 159 } 160 161 func (sh *SigHints) Refresh(m MetaContext) (err error) { 162 defer m.Trace(fmt.Sprintf("Refresh SigHints for uid=%s", sh.uid), &err)() 163 res, err := m.G().API.Get(m, APIArg{ 164 Endpoint: "sig/hints", 165 SessionType: APISessionTypeNONE, 166 Args: HTTPArgs{ 167 "uid": UIDArg(sh.uid), 168 "low": I{sh.version}, 169 }, 170 }) 171 if err != nil { 172 return err 173 } 174 175 return sh.RefreshWith(m, res.Body) 176 } 177 178 func (sh *SigHints) RefreshWith(m MetaContext, jw *jsonw.Wrapper) (err error) { 179 defer m.Trace("RefreshWith", &err)() 180 181 n, err := jw.AtKey("hints").Len() 182 if err != nil { 183 return err 184 } 185 if n == 0 { 186 m.Debug("| No changes; version %d was up-to-date", sh.version) 187 } else if err = sh.PopulateWith(jw); err != nil { 188 return err 189 } else { 190 sh.dirty = true 191 } 192 return nil 193 } 194 195 func LoadAndRefreshSigHints(m MetaContext, uid keybase1.UID) (*SigHints, error) { 196 sh, err := LoadSigHints(m, uid) 197 if err != nil { 198 return nil, err 199 } 200 if err = sh.Refresh(m); err != nil { 201 return nil, err 202 } 203 return sh, nil 204 }