pulumi/cmd/login.go
2017-11-02 11:19:00 -07:00

88 lines
2.5 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"
)
// PulumiAccessTokenEnvVar is the environment variable used to bypass a prompt on login.
const PulumiAccessTokenEnvVar = "PULUMI_ACCESS_TOKEN"
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 {
// 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.
accessToken := os.Getenv(PulumiAccessTokenEnvVar)
if accessToken != "" {
fmt.Printf("Using access token from %s.\n", PulumiAccessTokenEnvVar)
} else {
fmt.Print("Enter Pulumi access token: ")
reader := bufio.NewReader(os.Stdin)
raw, err := reader.ReadString('\n')
if err != 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
}