d5846d7e16
This PR adds `login` and `logout` commands to the `pulumi` CLI. Rather than requiring a user name and password like before, we instead require users to login with GitHub credentials on the Pulumi Console website. (You can do this now via https://beta.moolumi.io.) Once there, the account page will show you an "access token" you can use to authenticate against the CLI. Upon successful login, the user's credentials will be stored in `~/.pulumi/credentials.json`. This credentials file will be automatically read with the credentials added to every call to `PulumiRESTCall`.
93 lines
2.6 KiB
Go
93 lines
2.6 KiB
Go
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/pulumi/pulumi/pkg/apitype"
|
|
"github.com/pulumi/pulumi/pkg/util/cmdutil"
|
|
)
|
|
|
|
func newLoginCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "login",
|
|
Short: "Log into the Pulumi Cloud Console",
|
|
Long: "Log into the Pulumi Cloud Console. You can script by using PULUMI_ACCESS_TOKEN environment variable.",
|
|
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
|
return loginCmd()
|
|
}),
|
|
}
|
|
}
|
|
|
|
func newLogoutCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "logout",
|
|
Short: "Log out of the Pulumi CLI",
|
|
Long: "Log out of the Pulumi CLI. Deletes stored credentials on the local machine.",
|
|
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
|
|
return deleteStoredCredentials()
|
|
}),
|
|
}
|
|
}
|
|
|
|
// loginCmd is the implementation of the login command.
|
|
func loginCmd() error {
|
|
// Check if the the user is already logged in.
|
|
_, err := getStoredCredentials()
|
|
if err == nil {
|
|
return fmt.Errorf("already logged in")
|
|
}
|
|
|
|
// We intentionally don't accept command-line args for the user's access token. Having it in
|
|
// .bash_history is not great, and specifying it via flag isn't of much use.
|
|
//
|
|
// However, if PULUMI_ACCESS_TOKEN is available, we'll use that.
|
|
accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
|
|
if accessToken != "" {
|
|
fmt.Println("Using access token from PULUMI_ACCESS_TOKEN.")
|
|
} else {
|
|
fmt.Println("Enter Pulumi access token:")
|
|
reader := bufio.NewReader(os.Stdin)
|
|
raw, readErr := reader.ReadString('\n')
|
|
if readErr != nil {
|
|
return fmt.Errorf("reading STDIN: %v", err)
|
|
}
|
|
accessToken = strings.TrimSpace(raw)
|
|
}
|
|
|
|
// Try and use the credentials to see if they are valid.
|
|
valid, err := isValidAccessToken(accessToken)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !valid {
|
|
return fmt.Errorf("invalid access token")
|
|
}
|
|
|
|
// Save them.
|
|
creds := accountCredentials{
|
|
AccessToken: accessToken,
|
|
}
|
|
return storeCredentials(creds)
|
|
}
|
|
|
|
// isValidAccessToken tries to use the provided Pulumi access token and returns if it is accepted
|
|
// or not. Returns error on any unexpected error.
|
|
func isValidAccessToken(accessToken string) (bool, error) {
|
|
// Make a request to get the authenticated user. If it returns a successful result, the token
|
|
// checks out.
|
|
if err := pulumiRESTCallWithAccessToken("GET", "/user", nil, nil, accessToken); err != nil {
|
|
if errResp, ok := err.(*apitype.ErrorResponse); ok && errResp.Code == 401 {
|
|
return false, nil
|
|
}
|
|
return false, fmt.Errorf("testing access token: %v", err)
|
|
}
|
|
return true, nil
|
|
}
|