-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
engine.go
199 lines (172 loc) · 5.15 KB
/
engine.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
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.
package engine
import (
"fmt"
"runtime/debug"
"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol/keybase1"
)
type Prereqs = libkb.EnginePrereqs
type Engine2 = libkb.Engine2
type UIDelegateWanter interface {
WantDelegate(libkb.UIKind) bool
}
func requiresUI(c libkb.UIConsumer, kind libkb.UIKind) bool {
for _, ui := range c.RequiredUIs() {
if ui == kind {
return true
}
}
for _, sub := range c.SubConsumers() {
if requiresUI(sub, kind) {
return true
}
}
return false
}
// isLoggedInWithUIDAndError conveys if the user is in a logged-in state or not.
// If this function returns `true`, it's because the user is logged in,
// is on a provisioned device, and has an unlocked device key, If this
// function returns `false`, it's because either no one has ever logged onto
// this device, or someone has, and then clicked `logout`. If the return
// value is `false`, and `err` is `nil`, then the service is in one of
// those expected "logged out" states. If the return value is `false`
// and `err` is non-`nil`, then something went wrong, and the app is in some
// sort of unexpected state. If `ret` is `true`, then `uid` will convey
// which user is logged in.
//
// Under the hood, IsLoggedIn is going through the BootstrapActiveDevice
// flow and therefore will try its best to unlocked locked keys if it can
// without user interaction.
//
// If the user is intentionally not logged into any user, don't try to
// bootstrap from the secret store and just check if there is an active device.
func isLoggedInWithUIDAndError(m libkb.MetaContext) (ret bool, uid keybase1.UID, err error) {
if m.G().Env.GetStayLoggedOut() {
return m.ActiveDevice().Valid(), m.G().Env.GetUID(), nil
}
ret, uid, err = libkb.BootstrapActiveDeviceWithMetaContext(m)
return ret, uid, err
}
func isLoggedIn(m libkb.MetaContext) (ret bool, uid keybase1.UID) {
if m.G().Env.GetStayLoggedOut() {
return m.ActiveDevice().Valid(), m.G().Env.GetUID()
}
ret, uid, _ = libkb.BootstrapActiveDeviceWithMetaContext(m)
return ret, uid
}
func isLoggedInAs(m libkb.MetaContext, uid keybase1.UID) (ret bool) {
if m.G().Env.GetStayLoggedOut() {
return m.ActiveDevice().Valid() && uid == m.G().Env.GetUID()
}
ret, err := libkb.BootstrapActiveDeviceWithMetaContextAndAssertUID(m, uid)
if err != nil {
m.Debug("isLoggedAs error: %s", err)
}
return ret
}
func isLoggedInWithError(m libkb.MetaContext) (ret bool, err error) {
ret, _, err = isLoggedInWithUIDAndError(m)
return ret, err
}
func runPrereqs(m libkb.MetaContext, e Engine2) error {
prq := e.Prereqs()
if prq.TemporarySession {
if !m.HasAnySession() {
return libkb.NewLoginRequiredError("need either a temporary session or a device")
}
}
if prq.Device {
ok, err := isLoggedInWithError(m)
if err != nil {
return err
}
if !ok {
return libkb.DeviceRequiredError{}
}
}
return nil
}
func RunEngine2(m libkb.MetaContext, e Engine2) (err error) {
m = m.WithLogTag("ENG")
defer m.Trace(fmt.Sprintf("RunEngine(%s)", e.Name()), &err)()
if m, err = delegateUIs(m, e); err != nil {
return err
}
if err = check(m, e); err != nil {
return err
}
if err = runPrereqs(m, e); err != nil {
return err
}
err = e.Run(m)
return err
}
func getIdentifyUI3or1(m libkb.MetaContext) (libkb.IdentifyUI, error) {
uir := m.G().UIRouter
ret, err := uir.GetIdentify3UIAdapter(m)
if ret != nil && err == nil {
return ret, err
}
return uir.GetIdentifyUI()
}
func delegateUIs(m libkb.MetaContext, e Engine2) (libkb.MetaContext, error) {
if m.G().UIRouter == nil {
return m, nil
}
// currently, only doing this for SecretUI, but in future,
// perhaps should iterate over all registered UIs in UIRouter.
if requiresUI(e, libkb.SecretUIKind) {
sessionID := m.UIs().SessionID
if ui, err := m.G().UIRouter.GetSecretUI(sessionID); err != nil {
return m, err
} else if ui != nil {
m.Debug("using delegated secret UI for engine %q (session id = %d)", e.Name(), sessionID)
m = m.WithSecretUI(ui)
}
}
if wantsDelegateUI(e, libkb.IdentifyUIKind) {
m.Debug("IdentifyUI wanted for engine %q", e.Name())
ui, err := getIdentifyUI3or1(m)
if err != nil {
return m, err
}
if ui != nil {
m.Debug("using delegated identify UI for engine %q", e.Name())
m = m.WithDelegatedIdentifyUI(ui)
}
}
return m, nil
}
func wantsDelegateUI(e Engine2, kind libkb.UIKind) bool {
if !requiresUI(e, kind) {
return false
}
if i, ok := e.(UIDelegateWanter); ok {
return i.WantDelegate(kind)
}
return false
}
func check(m libkb.MetaContext, c libkb.UIConsumer) error {
if err := checkUI(m, c); err != nil {
return err
}
for _, sub := range c.SubConsumers() {
if err := check(m, sub); err != nil {
if _, ok := err.(CheckError); ok {
return err
}
return CheckError{fmt.Sprintf("%s: %s", sub.Name(), err)}
}
}
return nil
}
func checkUI(m libkb.MetaContext, c libkb.UIConsumer) error {
for _, ui := range c.RequiredUIs() {
if !m.UIs().HasUI(ui) {
return CheckError{fmt.Sprintf("%s: requires ui %q\n\n%s", c.Name(), ui, string(debug.Stack()))}
}
}
return nil
}