forked from keybase/client
/
login.go
162 lines (134 loc) · 4.2 KB
/
login.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
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.
// This is the main login engine.
package engine
import (
"errors"
"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol/keybase1"
)
var errNoConfig = errors.New("No user config available")
var errNoDevice = errors.New("No device provisioned locally for this user")
// Login is an engine.
type Login struct {
libkb.Contextified
deviceType string
usernameOrEmail string
clientType keybase1.ClientType
}
// NewLogin creates a Login engine. username is optional.
// deviceType should be libkb.DeviceTypeDesktop or
// libkb.DeviceTypeMobile.
func NewLogin(g *libkb.GlobalContext, deviceType string, usernameOrEmail string, ct keybase1.ClientType) *Login {
return &Login{
Contextified: libkb.NewContextified(g),
deviceType: deviceType,
usernameOrEmail: usernameOrEmail,
clientType: ct,
}
}
// Name is the unique engine name.
func (e *Login) Name() string {
return "Login"
}
// GetPrereqs returns the engine prereqs.
func (e *Login) Prereqs() Prereqs {
return Prereqs{}
}
// RequiredUIs returns the required UIs.
func (e *Login) RequiredUIs() []libkb.UIKind {
return []libkb.UIKind{}
}
// SubConsumers returns the other UI consumers for this engine.
func (e *Login) SubConsumers() []libkb.UIConsumer {
return []libkb.UIConsumer{
&LoginProvisionedDevice{},
&loginLoadUser{},
&loginProvision{},
}
}
// Run starts the engine.
func (e *Login) Run(ctx *Context) error {
if len(e.usernameOrEmail) > 0 && libkb.CheckEmail.F(e.usernameOrEmail) {
// If e.usernameOrEmail is provided and it is an email address, then
// loginProvisionedDevice is pointless. It would return an error,
// but might as well not even use it.
e.G().Log.Debug("skipping loginProvisionedDevice since %q provided to Login, which looks like an email address.", e.usernameOrEmail)
} else {
// First see if this device is already provisioned and it is possible to log in.
eng := NewLoginProvisionedDevice(e.G(), e.usernameOrEmail)
err := RunEngine(eng, ctx)
if err == nil {
// login successful
e.G().Log.Debug("LoginProvisionedDevice.Run() was successful")
e.sendNotification()
return nil
}
// if this device has been provisioned already and there was an error, then
// return that error. Otherwise, ignore it and keep going.
if !e.notProvisionedErr(err) {
return err
}
e.G().Log.Debug("loginProvisionedDevice error: %s (continuing with device provisioning...)", err)
}
e.G().Log.Debug("attempting device provisioning")
// clear out any existing session:
e.G().Logout()
// transaction around config file
tx, err := e.G().Env.GetConfigWriter().BeginTransaction()
if err != nil {
return err
}
// From this point on, if there's an error, we abort the
// transaction.
defer func() {
if tx != nil {
tx.Abort()
}
}()
// run the LoginLoadUser sub-engine to load a user
ueng := newLoginLoadUser(e.G(), e.usernameOrEmail)
if err = RunEngine(ueng, ctx); err != nil {
return err
}
// make sure the user isn't already provisioned (can
// get here if usernameOrEmail is an email address
// for an already provisioned on this device user).
if ueng.User().HasCurrentDeviceInCurrentInstall() {
return libkb.DeviceAlreadyProvisionedError{}
}
darg := &loginProvisionArg{
DeviceType: e.deviceType,
ClientType: e.clientType,
User: ueng.User(),
}
deng := newLoginProvision(e.G(), darg)
if err = RunEngine(deng, ctx); err != nil {
return err
}
// commit the config changes
if err := tx.Commit(); err != nil {
return err
}
// Zero out the TX so that we don't abort it in the defer()
// exit.
tx = nil
e.sendNotification()
return nil
}
// notProvisionedErr will return true if err signifies that login
// failed because this device has not yet been provisioned.
func (e *Login) notProvisionedErr(err error) bool {
if err == errNoDevice {
return true
}
if err == errNoConfig {
return true
}
e.G().Log.Debug("notProvisioned, not handling error %s (err type: %T)", err, err)
return false
}
func (e *Login) sendNotification() {
e.G().NotifyRouter.HandleLogin(string(e.G().Env.GetUsername()))
e.G().CallLoginHooks()
}