-
Notifications
You must be signed in to change notification settings - Fork 894
feat: Login via CLI #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Login via CLI #298
Changes from 1 commit
6c50b85
fe55c73
3bb3164
9ec938e
87fc9e2
6607a5a
0e323ce
a33d2c7
9cc7e94
7774e29
8ea2920
669f2ca
0a43941
5ecba6d
13696a7
dccb009
ca1a458
b232d0c
0d829b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,20 +2,30 @@ package cli | |
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"net/url" | ||
"os/exec" | ||
"os/user" | ||
"runtime" | ||
"strings" | ||
|
||
"github.com/fatih/color" | ||
"github.com/go-playground/validator/v10" | ||
"github.com/manifoldco/promptui" | ||
"github.com/pkg/browser" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/xerrors" | ||
|
||
"github.com/coder/coder/coderd" | ||
"github.com/coder/coder/codersdk" | ||
) | ||
|
||
const ( | ||
goosWindows = "windows" | ||
goosLinux = "linux" | ||
goosDarwin = "darwin" | ||
) | ||
|
||
func login() *cobra.Command { | ||
return &cobra.Command{ | ||
Use: "login <url>", | ||
|
@@ -116,21 +126,99 @@ func login() *cobra.Command { | |
if err != nil { | ||
return xerrors.Errorf("login with password: %w", err) | ||
} | ||
config := createConfig(cmd) | ||
err = config.Session().Write(resp.SessionToken) | ||
if err != nil { | ||
return xerrors.Errorf("write session token: %w", err) | ||
} | ||
err = config.URL().Write(serverURL.String()) | ||
|
||
err = saveSessionToken(cmd, client, resp.SessionToken, serverURL) | ||
if err != nil { | ||
return xerrors.Errorf("write server url: %w", err) | ||
return xerrors.Errorf("save session token: %w", err) | ||
} | ||
|
||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", color.HiBlackString(">"), color.HiCyanString(username)) | ||
return nil | ||
} | ||
|
||
authURL := *serverURL | ||
authURL.Path = serverURL.Path + "/cli-auth" | ||
if err := openURL(authURL.String()); err != nil { | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Open the following in your browser:\n\n\t%s\n\n", authURL.String()) | ||
} else { | ||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Your browser has been opened to visit:\n\n\t%s\n\n", authURL.String()) | ||
} | ||
|
||
apiKey, err := prompt(cmd, &promptui.Prompt{ | ||
Label: "Paste your token here:", | ||
Validate: func(token string) error { | ||
client.SessionToken = token | ||
_, err := client.User(cmd.Context(), "me") | ||
if err != nil { | ||
return xerrors.New("That's not a valid token!") | ||
} | ||
return err | ||
}, | ||
}) | ||
if err != nil { | ||
return xerrors.Errorf("specify email prompt: %w", err) | ||
} | ||
|
||
err = saveSessionToken(cmd, client, apiKey, serverURL) | ||
if err != nil { | ||
return xerrors.Errorf("save session token after login: %w", err) | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
} | ||
|
||
func saveSessionToken(cmd *cobra.Command, client *codersdk.Client, sessionToken string, serverURL *url.URL) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm hesitant to rip this into a distinct function. We already validate the "me" user request above, so feels like that should be reused. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The alternative is to duplicate the persistence code both in the 'create initial user' and 'login' flows - inlined this function in 13696a7 Another thing we could consider is bringing the |
||
// Login to get user data - verify it is OK before persisting | ||
client.SessionToken = sessionToken | ||
resp, err := client.User(cmd.Context(), "me") | ||
if err != nil { | ||
return xerrors.Errorf("get user: ", err) | ||
} | ||
|
||
config := createConfig(cmd) | ||
err = config.Session().Write(sessionToken) | ||
if err != nil { | ||
return xerrors.Errorf("write session token: %w", err) | ||
} | ||
err = config.URL().Write(serverURL.String()) | ||
if err != nil { | ||
return xerrors.Errorf("write server url: %w", err) | ||
} | ||
|
||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", color.HiBlackString(">"), color.HiCyanString(resp.Username)) | ||
return nil | ||
} | ||
|
||
// isWSL determines if coder-cli is running within Windows Subsystem for Linux | ||
func isWSL() (bool, error) { | ||
if runtime.GOOS == goosDarwin || runtime.GOOS == goosWindows { | ||
return false, nil | ||
} | ||
data, err := ioutil.ReadFile("/proc/version") | ||
if err != nil { | ||
return false, xerrors.Errorf("read /proc/version: %w", err) | ||
} | ||
return strings.Contains(strings.ToLower(string(data)), "microsoft"), nil | ||
} | ||
|
||
// openURL opens the provided URL via user's default browser | ||
func openURL(url string) error { | ||
var cmd string | ||
var args []string | ||
|
||
wsl, err := isWSL() | ||
if err != nil { | ||
return xerrors.Errorf("test running Windows Subsystem for Linux: %w", err) | ||
} | ||
|
||
if wsl { | ||
cmd = "cmd.exe" | ||
args = []string{"/c", "start"} | ||
url = strings.ReplaceAll(url, "&", "^&") | ||
args = append(args, url) | ||
return exec.Command(cmd, args...).Start() | ||
} | ||
|
||
return browser.OpenURL(url) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.