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