/
login.go
145 lines (125 loc) · 3.4 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
package session
import (
"fmt"
"os"
"github.com/Scalingo/cli/config"
"github.com/Scalingo/cli/io"
netssh "github.com/Scalingo/cli/net/ssh"
"github.com/Scalingo/go-scalingo"
"github.com/Scalingo/go-scalingo/debug"
"github.com/Scalingo/go-utils/errors"
"gopkg.in/errgo.v1"
)
type LoginOpts struct {
APIToken string
PasswordOnly bool
SSH bool
SSHIdentity string
}
func Login(opts LoginOpts) error {
if opts.SSHIdentity == "" {
opts.SSHIdentity = "ssh-agent"
}
if opts.APIToken != "" {
return loginWithToken(opts.APIToken)
}
if !opts.PasswordOnly {
io.Info("Trying login with SSH…")
err := loginWithSSH(opts.SSHIdentity)
if err != nil {
config.C.Logger.Printf("SSH connection failed: %+v\n", err)
io.Error("SSH connection failed.")
if opts.SSH {
if errors.ErrgoRoot(err) == netssh.ErrNoAuthSucceed {
return errgo.Notef(err, "please use the flag '--ssh-identity /path/to/private/key' to specify your private key")
}
return errgo.Notef(err, "fail to login with SSH")
}
} else {
return nil
}
}
io.Info("Trying login with user/password:\n")
return loginWithUserAndPassword()
}
func loginWithUserAndPassword() error {
_, _, err := config.Auth()
if err != nil {
return errgo.Mask(err, errgo.Any)
}
return nil
}
func loginWithToken(token string) error {
err := finalizeLogin(token)
if err != nil {
return errgo.Notef(err, "token invalid")
}
return nil
}
func loginWithSSH(identity string) error {
debug.Println("Login through SSH, identity:", identity)
client, _, err := netssh.Connect(netssh.ConnectOpts{
Host: config.C.ScalingoSshHost,
Identity: identity,
})
if err != nil {
return errgo.Notef(err, "fail to connect to SSH server")
}
channel, reqs, err := client.OpenChannel("session", []byte{})
if err != nil {
return errgo.Notef(err, "fail to open SSH channel")
}
defer client.Close()
_, err = channel.SendRequest("auth.v2@scalingo.com", false, []byte{})
if err != nil {
return errgo.Notef(err, "SSH authentication request fails")
}
req := <-reqs
if req == nil {
return errgo.Newf("invalid response from auth request")
}
if req.Type != "auth.v2@scalingo.com" {
return errgo.Newf("invalid response from SSH server, type is %v", req.Type)
}
payload := req.Payload
if len(payload) == 0 {
return errgo.Newf("invalid response from SSH server")
}
hostname, err := os.Hostname()
if err != nil {
return errgo.Notef(err, "fail to get current hostname")
}
c, err := config.ScalingoUnauthenticatedAuthClient()
if err != nil {
return errgo.Notef(err, "fail to create an unauthenticated Scalingo client")
}
token, err := c.TokenCreateWithLogin(scalingo.TokenCreateParams{
Name: fmt.Sprintf("Scalingo CLI - %s", hostname),
}, scalingo.LoginParams{
JWT: string(payload),
})
if err != nil {
return errgo.NoteMask(err, "fail to create API token", errgo.Any)
}
err = finalizeLogin(token.Token)
if err != nil {
return errgo.NoteMask(err, "fail to finalize login", errgo.Any)
}
return nil
}
func finalizeLogin(token string) error {
c, err := config.ScalingoAuthClientFromToken(token)
if err != nil {
return errgo.Notef(err, "fail to create an authenticated Scalingo client using the API token")
}
user, err := c.Self()
if err != nil {
return errgo.Mask(err)
}
io.Statusf("Hello %s, nice to see you!\n", user.Username)
err = config.SetCurrentUser(user, token)
if err != nil {
return errgo.Mask(err)
}
return nil
}