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