1// Copyright 2015 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4package engine 5 6import ( 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 15type 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 29var _ (Engine2) = (*ResolveThenIdentify2)(nil) 30 31func NewResolveThenIdentify2(g *libkb.GlobalContext, arg *keybase1.Identify2Arg) *ResolveThenIdentify2 { 32 return &ResolveThenIdentify2{ 33 Contextified: libkb.NewContextified(g), 34 arg: arg, 35 } 36} 37 38func 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. 47func (e *ResolveThenIdentify2) Name() string { 48 return "ResolveThenIdentify2" 49} 50 51// GetPrereqs returns the engine prereqs. 52func (e *ResolveThenIdentify2) Prereqs() Prereqs { 53 return Prereqs{} 54} 55 56// RequiredUIs returns the required UIs. 57func (e *ResolveThenIdentify2) RequiredUIs() []libkb.UIKind { 58 return []libkb.UIKind{} 59} 60 61// SubConsumers returns the other UI consumers for this engine. 62func (e *ResolveThenIdentify2) SubConsumers() []libkb.UIConsumer { 63 return []libkb.UIConsumer{ 64 &Identify2WithUID{ 65 arg: e.arg, 66 }, 67 } 68} 69 70func (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 107func (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 124func (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 151func (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 158func (e *ResolveThenIdentify2) SetResponsibleGregorItem(item gregor.Item) { 159 e.responsibleGregorItem = item 160} 161 162func (e *ResolveThenIdentify2) TrackToken() keybase1.TrackToken { 163 return e.i2eng.TrackToken() 164} 165 166func (e *ResolveThenIdentify2) ConfirmResult() keybase1.ConfirmResult { 167 return e.i2eng.ConfirmResult() 168} 169 170func (e *ResolveThenIdentify2) GetProofSet() *libkb.ProofSet { 171 if e.i2eng == nil { 172 return nil 173 } 174 return e.i2eng.GetProofSet() 175} 176 177func (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. 191func 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} 214