github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/identify_outcome.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 "sort" 9 "strings" 10 11 "github.com/keybase/client/go/gregor" 12 keybase1 "github.com/keybase/client/go/protocol/keybase1" 13 jsonw "github.com/keybase/go-jsonw" 14 ) 15 16 type IdentifyOutcome struct { 17 Contextified 18 Username NormalizedUsername 19 UID keybase1.UID 20 EldestSeqno keybase1.Seqno 21 Error error 22 KeyDiffs []TrackDiff 23 Revoked []TrackDiff 24 RevokedDetails []keybase1.RevokedProof 25 ProofChecks []*LinkCheckResult 26 Warnings []Warning 27 TrackUsed *TrackLookup 28 TrackEqual bool // Whether the track statement was equal to what we saw 29 TrackOptions keybase1.TrackOptions 30 Reason keybase1.IdentifyReason 31 ResponsibleGregorItem gregor.Item 32 } 33 34 func NewIdentifyOutcome(g *GlobalContext, username NormalizedUsername, uid keybase1.UID, eldestSeqno keybase1.Seqno) *IdentifyOutcome { 35 return &IdentifyOutcome{ 36 Contextified: NewContextified(g), 37 Username: username, 38 UID: uid, 39 EldestSeqno: eldestSeqno, 40 } 41 } 42 43 func (i *IdentifyOutcome) remoteProofLinks() *RemoteProofLinks { 44 rpl := NewRemoteProofLinks(i.G()) 45 for _, p := range i.ProofChecks { 46 rpl.Insert(p.link, p.err) 47 } 48 return rpl 49 } 50 51 func (i *IdentifyOutcome) GetRemoteCheckResultFor(service string, username string) ProofError { 52 cieq := strings.EqualFold 53 for _, pc := range i.ProofChecks { 54 k, v := pc.GetLink().ToKeyValuePair() 55 if cieq(k, service) && cieq(v, username) { 56 return pc.GetProofError() 57 } 58 } 59 return NewProofError(keybase1.ProofStatus_NO_HINT, "no proof checked for %s@%s", username, service) 60 } 61 62 func (i *IdentifyOutcome) ActiveProofs() []RemoteProofChainLink { 63 return i.remoteProofLinks().Active() 64 } 65 66 func (i *IdentifyOutcome) AddProofsToSet(existing *ProofSet, okStates []keybase1.ProofState) { 67 i.remoteProofLinks().AddProofsToSet(existing, okStates) 68 } 69 70 func (i *IdentifyOutcome) TrackSet() *TrackSet { 71 return i.remoteProofLinks().TrackSet() 72 } 73 74 func (i *IdentifyOutcome) ProofChecksSorted(mctx MetaContext) []*LinkCheckResult { 75 // Sort by display priority 76 pc := make([]*LinkCheckResult, len(i.ProofChecks)) 77 copy(pc, i.ProofChecks) 78 proofServices := i.G().GetProofServices() 79 serviceTypes := map[string]int{} 80 for _, lcr := range pc { 81 key := lcr.link.DisplayPriorityKey() 82 if _, ok := serviceTypes[key]; !ok { 83 st := proofServices.GetServiceType(mctx.Ctx(), key) 84 displayPriority := 0 85 if st != nil { 86 displayPriority = st.DisplayPriority() 87 } 88 serviceTypes[key] = displayPriority 89 } 90 } 91 sort.Slice(pc, func(a, b int) bool { 92 keyA := pc[a].link.DisplayPriorityKey() 93 keyB := pc[b].link.DisplayPriorityKey() 94 return serviceTypes[keyA] > serviceTypes[keyB] 95 }) 96 return pc 97 } 98 99 // Revoked proofs are those we used to look for but are gone! 100 func (i IdentifyOutcome) NumRevoked() int { 101 return len(i.Revoked) 102 } 103 104 // The number of proofs that failed. 105 func (i IdentifyOutcome) NumProofFailures() int { 106 nfails := 0 107 for _, c := range i.ProofChecks { 108 if c.err != nil { 109 nfails++ 110 } 111 } 112 return nfails 113 } 114 115 // The number of proofs that actually worked 116 func (i IdentifyOutcome) NumProofSuccesses() int { 117 nsucc := 0 118 for _, c := range i.ProofChecks { 119 if c.err == nil { 120 nsucc++ 121 } 122 } 123 return nsucc 124 } 125 126 // A "Track Failure" is when we previously tracked this user, and 127 // some aspect of their proof changed. Like their key changed, or 128 // they changed Twitter names 129 func (i IdentifyOutcome) NumTrackFailures() int { 130 ntf := 0 131 check := func(d TrackDiff) bool { 132 return d != nil && d.BreaksTracking() 133 } 134 for _, c := range i.ProofChecks { 135 if check(c.diff) || check(c.remoteDiff) { 136 ntf++ 137 } 138 } 139 140 for _, k := range i.KeyDiffs { 141 if check(k) { 142 ntf++ 143 } 144 } 145 146 return ntf 147 } 148 149 // A "Track Change" isn't necessary a failure, maybe they upgraded 150 // a proof from HTTP to HTTPS. But we still should retrack if we can. 151 func (i IdentifyOutcome) NumTrackChanges() int { 152 ntc := 0 153 check := func(d TrackDiff) bool { 154 return d != nil && !d.IsSameAsTracked() 155 } 156 for _, c := range i.ProofChecks { 157 if check(c.diff) || check(c.remoteDiff) { 158 ntc++ 159 } 160 } 161 for _, k := range i.KeyDiffs { 162 if check(k) { 163 ntc++ 164 } 165 } 166 return ntc 167 } 168 169 func (i IdentifyOutcome) TrackStatus() keybase1.TrackStatus { 170 if i.NumRevoked() > 0 { 171 return keybase1.TrackStatus_UPDATE_BROKEN_REVOKED 172 } 173 if i.NumTrackFailures() > 0 { 174 return keybase1.TrackStatus_UPDATE_BROKEN_FAILED_PROOFS 175 } 176 if i.TrackUsed != nil { 177 if i.NumTrackChanges() > 0 { 178 return keybase1.TrackStatus_UPDATE_NEW_PROOFS 179 } 180 if i.NumTrackChanges() == 0 { 181 return keybase1.TrackStatus_UPDATE_OK 182 } 183 } 184 if i.NumProofSuccesses() == 0 { 185 return keybase1.TrackStatus_NEW_ZERO_PROOFS 186 } 187 if i.NumProofFailures() > 0 { 188 return keybase1.TrackStatus_NEW_FAIL_PROOFS 189 } 190 return keybase1.TrackStatus_NEW_OK 191 } 192 193 func (i IdentifyOutcome) IsOK() bool { 194 switch i.TrackStatus() { 195 case keybase1.TrackStatus_UPDATE_NEW_PROOFS: 196 return true 197 case keybase1.TrackStatus_UPDATE_OK: 198 return true 199 case keybase1.TrackStatus_NEW_ZERO_PROOFS: 200 return true 201 case keybase1.TrackStatus_NEW_OK: 202 return true 203 default: 204 return false 205 } 206 } 207 208 func (i IdentifyOutcome) TrackingStatement() *jsonw.Wrapper { 209 return i.remoteProofLinks().TrackingStatement() 210 } 211 212 func (i IdentifyOutcome) GetErrorAndWarnings(strict bool) (warnings Warnings, err error) { 213 214 if i.Error != nil { 215 err = i.Error 216 return warnings, err 217 } 218 219 var probs []string 220 221 softErr := func(s string) { 222 if strict { 223 probs = append(probs, s) 224 } else { 225 warnings.Push(StringWarning(s)) 226 } 227 } 228 229 // For revoked proofs, we almost always want to return an error. The one exception 230 // is a snoozed tracker proof in non-strict mode. 231 for _, revoked := range i.Revoked { 232 errString := revoked.ToDisplayString() 233 isSnoozed := false 234 if _, ok := revoked.(TrackDiffSnoozedRevoked); ok { 235 isSnoozed = true 236 } 237 238 if !strict && isSnoozed { 239 warnings.Push(StringWarning(errString)) 240 } else { 241 probs = append(probs, errString) 242 } 243 } 244 245 if nfails := i.NumProofFailures(); nfails > 0 { 246 p := fmt.Sprintf("PROBLEM: %d proof%s failed remote checks", nfails, GiveMeAnS(nfails)) 247 softErr(p) 248 } 249 250 if ntf := i.NumTrackFailures(); ntf > 0 { 251 probs = append(probs, 252 fmt.Sprintf("%d followed proof%s failed", 253 ntf, GiveMeAnS(ntf))) 254 } 255 256 if len(probs) > 0 { 257 err = IdentifySummaryError{i.Username, probs} 258 } 259 260 return warnings, err 261 } 262 263 func (i IdentifyOutcome) GetError() error { 264 _, e := i.GetErrorAndWarnings(true /*strict */) 265 return e 266 } 267 268 func (i IdentifyOutcome) GetErrorLax() (Warnings, error) { 269 return i.GetErrorAndWarnings(false /*strict */) 270 }