<commit_before>package cmd

import (
	bigv "bigv.io/client/lib"
	"bufio"
	"fmt"
	"github.com/bgentry/speakeasy"
	"os"
	"strings"
)

type Commands interface {
	EnsureAuth()

	Config([]string)
	Debug([]string)
	Help([]string)
	ShowAccount([]string)
	ShowVM([]string)

	HelpForConfig()
	HelpForHelp()
	HelpForShow()
}

type CommandSet struct {
	bigv   bigv.Client
	config ConfigManager
}

func NewCommandSet(config ConfigManager, client bigv.Client) *CommandSet {
	commandSet := new(CommandSet)
	commandSet.config = config
	commandSet.bigv = client
	return commandSet
}

func (cmds *CommandSet) EnsureAuth() {
	err := cmds.bigv.AuthWithToken(cmds.config.Get("token"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to use token, trying credentials.\r\n\r\n")
		cmds.PromptForCredentials()
		credents := map[string]string{
			"username": cmds.config.Get("user"),
			"password": cmds.config.Get("pass"),
		}
		if cmds.config.Get("yubikey") != "" {
			credents["yubikey"] = cmds.config.Get("yubikey-otp")
		}

		err = cmds.bigv.AuthWithCredentials(credents)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to use credentials.\r\n")
			panic(err)
		}
	}

	cmds.config.SetPersistent("token", cmds.bigv.GetSessionToken(), "AUTH")
}

// PromptForCredentials ensures that user, pass and yubikey-otp are defined, by prompting the user for them.
// needs a for loop to ensure that they don't stay empty.
func (cmds *CommandSet) PromptForCredentials() {
	buf := bufio.NewReader(os.Stdin)
	for cmds.config.Get("user") == "" {
		fmt.Fprintf(os.Stderr, "User: ")
		user, _ := buf.ReadString('\n')
		cmds.config.Set("user", strings.TrimSpace(user), "INTERACTION")
		fmt.Fprintf(os.Stderr, "\r\n")
	}

	for cmds.config.Get("pass") == "" {
		pass, err := speakeasy.Ask("Pass: ")
<commit_msg>Prompt user for credentials 3 times then give up. Fixes #10378<commit_after>package cmd

import (
	bigv "bigv.io/client/lib"
	"bufio"
	"fmt"
	"github.com/bgentry/speakeasy"
	"os"
	"strings"
)

type Commands interface {
	EnsureAuth()

	Config([]string)
	Debug([]string)
	Help([]string)
	ShowAccount([]string)
	ShowVM([]string)

	HelpForConfig()
	HelpForHelp()
	HelpForShow()
}

type CommandSet struct {
	bigv   bigv.Client
	config ConfigManager
}

func NewCommandSet(config ConfigManager, client bigv.Client) *CommandSet {
	commandSet := new(CommandSet)
	commandSet.config = config
	commandSet.bigv = client
	return commandSet
}

func (cmds *CommandSet) EnsureAuth() {
	err := cmds.bigv.AuthWithToken(cmds.config.Get("token"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to use token, trying credentials.\r\n\r\n")
		attempts := 3

		for err != nil {
			attempts--

			cmds.PromptForCredentials()
			credents := map[string]string{
				"username": cmds.config.Get("user"),
				"password": cmds.config.Get("pass"),
			}
			if cmds.config.Get("yubikey") != "" {
				credents["yubikey"] = cmds.config.Get("yubikey-otp")
			}

			err = cmds.bigv.AuthWithCredentials(credents)
			if err == nil {
				// sucess!
				cmds.config.SetPersistent("token", cmds.bigv.GetSessionToken(), "AUTH")
				break
			} else {
				if strings.Contains(err.Error(), "Badly-formed parameters") || strings.Contains(err.Error(), "Bad login credentials") {
					if attempts > 0 {
						fmt.Fprintf(os.Stderr, "Invalid credentials, please try again\r\n")
						cmds.config.Set("user", "", "INVALID")
						cmds.config.Set("pass", "", "INVALID")
						cmds.config.Set("yubikey-otp", "", "INVALID")
					} else {
						fmt.Fprintf(os.Stderr, "Invalid credentials, giving up after three attempts.\r\n")
						// TODO(telyn): define exit codes
						os.Exit(1)
					}
				} else {
					panic(err)
				}

			}
		}
	}

}

// PromptForCredentials ensures that user, pass and yubikey-otp are defined, by prompting the user for them.
// needs a for loop to ensure that they don't stay empty.
func (cmds *CommandSet) PromptForCredentials() {
	buf := bufio.NewReader(os.Stdin)
	for cmds.config.Get("user") == "" {
		fmt.Fprintf(os.Stderr, "User: ")
		user, _ := buf.ReadString('\n')
		cmds.config.Set("user", strings.TrimSpace(user), "INTERACTION")
		fmt.Fprintf(os.Stderr, "\r\n")
	}

	for cmds.config.Get("pass") == "" {
		pass, err := speakeasy.Ask("Pass: ")

{'fraction_non_alphanumeric': 0.16107542231739233, 'fraction_numerical': 0.0021413276231263384, 'mean_word_length': 3.8940628637951105, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4843690', 'n_tokens_mistral': 1682, 'n_tokens_neox': 1537, 'n_words': 316}