github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/resolve_identify2.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 engine 5 6 import ( 7 "errors" 8 "fmt" 9 10 gregor "github.com/keybase/client/go/gregor" 11 "github.com/keybase/client/go/libkb" 12 keybase1 "github.com/keybase/client/go/protocol/keybase1" 13 ) 14 15 type ResolveThenIdentify2 struct { 16 libkb.Contextified 17 arg *keybase1.Identify2Arg 18 i2eng *Identify2WithUID 19 testArgs *Identify2WithUIDTestArgs 20 responsibleGregorItem gregor.Item 21 queriedName libkb.NormalizedUsername 22 23 // When tracking is being performed, the identify engine is used with a tracking ui. 24 // These options are sent to the ui based on command line options. 25 // For normal identify, safe to leave these in their default zero state. 26 trackOptions keybase1.TrackOptions 27 } 28 29 var _ (Engine2) = (*ResolveThenIdentify2)(nil) 30 31 func NewResolveThenIdentify2(g *libkb.GlobalContext, arg *keybase1.Identify2Arg) *ResolveThenIdentify2 { 32 return &ResolveThenIdentify2{ 33 Contextified: libkb.NewContextified(g), 34 arg: arg, 35 } 36 } 37 38 func NewResolveThenIdentify2WithTrack(g *libkb.GlobalContext, arg *keybase1.Identify2Arg, topts keybase1.TrackOptions) *ResolveThenIdentify2 { 39 return &ResolveThenIdentify2{ 40 Contextified: libkb.NewContextified(g), 41 arg: arg, 42 trackOptions: topts, 43 } 44 } 45 46 // Name is the unique engine name. 47 func (e *ResolveThenIdentify2) Name() string { 48 return "ResolveThenIdentify2" 49 } 50 51 // GetPrereqs returns the engine prereqs. 52 func (e *ResolveThenIdentify2) Prereqs() Prereqs { 53 return Prereqs{} 54 } 55 56 // RequiredUIs returns the required UIs. 57 func (e *ResolveThenIdentify2) RequiredUIs() []libkb.UIKind { 58 return []libkb.UIKind{} 59 } 60 61 // SubConsumers returns the other UI consumers for this engine. 62 func (e *ResolveThenIdentify2) SubConsumers() []libkb.UIConsumer { 63 return []libkb.UIConsumer{ 64 &Identify2WithUID{ 65 arg: e.arg, 66 }, 67 } 68 } 69 70 func (e *ResolveThenIdentify2) resolveUID(m libkb.MetaContext) (err error) { 71 if !e.arg.Uid.IsNil() { 72 return nil 73 } 74 75 // if no uid and no assertion, then if logged in use self uid: 76 if len(e.arg.UserAssertion) == 0 && e.arg.AllowEmptySelfID { 77 ok, uid := isLoggedIn(m) 78 if ok { 79 e.arg.Uid = uid 80 return nil 81 } 82 return libkb.LoginRequiredError{Context: "to identify without specifying a user assertion"} 83 } 84 85 rres := m.G().Resolver.ResolveFullExpressionWithBody(m, e.arg.UserAssertion) 86 if err = rres.GetError(); err != nil { 87 m.Debug("ResolveThenIdentify2#resolveUID: failing assertion for arg %+v", e.arg) 88 return err 89 } 90 e.arg.Uid = rres.GetUID() 91 if rres.WasKBAssertion() && !e.arg.NeedProofSet { 92 m.Debug("Assertion was 'KB' and we don't need proofset: %s", e.arg.UserAssertion) 93 // the resolve assertion was a keybase username or UID, so remove it 94 // from identify2 arg to allow cache hits on UID. 95 e.arg.UserAssertion = "" 96 // But still check on that way out that the username matches the UID 97 e.queriedName = rres.GetNormalizedQueriedUsername() 98 } 99 100 // An optimization --- plumb through the resolve body for when we load the 101 // user. This will save a round trip to the server. 102 e.i2eng.ResolveBody = rres.GetBody() 103 104 return nil 105 } 106 107 func (e *ResolveThenIdentify2) nameResolutionPostAssertion(m libkb.MetaContext) (err error) { 108 // Check the server for cheating on a Name->UID resolution. After we do a userload (by UID), 109 // we should have a merkle-verified idea of what the corresponding name is, so we check it 110 // as a post-assertion here. 111 if e.queriedName.IsNil() { 112 return nil 113 } 114 res, err := e.Result(m) 115 if err != nil { 116 return err 117 } 118 if !libkb.NewNormalizedUsername(res.Upk.GetName()).Eq(e.queriedName) { 119 return libkb.NewUIDMismatchError("bad user returned for " + e.queriedName.String()) 120 } 121 return nil 122 } 123 124 func (e *ResolveThenIdentify2) Run(m libkb.MetaContext) (err error) { 125 m = m.WithLogTag("ID2") 126 127 defer m.Trace("ResolveThenIdentify2#Run", &err)() 128 129 e.i2eng = NewIdentify2WithUID(m.G(), e.arg) 130 if e.responsibleGregorItem != nil { 131 e.i2eng.SetResponsibleGregorItem(e.responsibleGregorItem) 132 } 133 e.i2eng.trackOptions = e.trackOptions 134 135 if err = e.resolveUID(m); err != nil { 136 return err 137 } 138 139 // For testing 140 e.i2eng.testArgs = e.testArgs 141 if err = RunEngine2(m, e.i2eng); err != nil { 142 return err 143 } 144 145 if err = e.nameResolutionPostAssertion(m); err != nil { 146 return err 147 } 148 return nil 149 } 150 151 func (e *ResolveThenIdentify2) Result(m libkb.MetaContext) (*keybase1.Identify2ResUPK2, error) { 152 if e.i2eng == nil { 153 return nil, errors.New("ResolveThenIdentify2#Result: no result available if the engine did not run") 154 } 155 return e.i2eng.Result(m) 156 } 157 158 func (e *ResolveThenIdentify2) SetResponsibleGregorItem(item gregor.Item) { 159 e.responsibleGregorItem = item 160 } 161 162 func (e *ResolveThenIdentify2) TrackToken() keybase1.TrackToken { 163 return e.i2eng.TrackToken() 164 } 165 166 func (e *ResolveThenIdentify2) ConfirmResult() keybase1.ConfirmResult { 167 return e.i2eng.ConfirmResult() 168 } 169 170 func (e *ResolveThenIdentify2) GetProofSet() *libkb.ProofSet { 171 if e.i2eng == nil { 172 return nil 173 } 174 return e.i2eng.GetProofSet() 175 } 176 177 func (e *ResolveThenIdentify2) GetIdentifyOutcome() *libkb.IdentifyOutcome { 178 if e.i2eng == nil { 179 return nil 180 } 181 return e.i2eng.GetIdentifyOutcome() 182 } 183 184 // ResolveAndCheck takes as input a name (joe), social assertion (joe@twitter) 185 // or compound assertion (joe+joe@twitter+3883883773222@pgp) and resolves 186 // it to a user, verifying the result. Pass into it a MetaContext without any UIs set, 187 // since it is meant to run without any UI interaction. Tracking statements 188 // are optionally taken into account (see flag). No ID2-specific caching will be used, 189 // but the UPAK cache will be used, and busted with ForceRepoll semantics. The output, on success, 190 // is a populated UserPlusKeysV2. 191 func ResolveAndCheck(m libkb.MetaContext, s string, useTracking bool) (ret keybase1.UserPlusKeysV2, err error) { 192 193 m = m.WithLogTag("RAC") 194 defer m.Trace(fmt.Sprintf("ResolveAndCheck(%q,%t)", s, useTracking), &err)() 195 196 arg := keybase1.Identify2Arg{ 197 UserAssertion: s, 198 CanSuppressUI: true, 199 ActLoggedOut: !useTracking, 200 NoErrorOnTrackFailure: !useTracking, 201 IdentifyBehavior: keybase1.TLFIdentifyBehavior_RESOLVE_AND_CHECK, 202 } 203 eng := NewResolveThenIdentify2(m.G(), &arg) 204 if err = RunEngine2(m, eng); err != nil { 205 return ret, err 206 } 207 res, err := eng.Result(m) 208 if err != nil { 209 return ret, err 210 } 211 // Success path. 212 return res.Upk.Current, nil 213 }