github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }