fix: read pwd from non-tty input (#996)

Signed-off-by: Matej Vasek <mvasek@redhat.com>
This commit is contained in:
Matej Vasek 2022-04-28 14:09:53 +02:00 committed by GitHub
parent b79683cc0c
commit e9932cdf43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 10 deletions

View File

@ -119,7 +119,7 @@ func newTransport() fnhttp.RoundTripCloser {
// of features or configuration nuances of cluster variants. // of features or configuration nuances of cluster variants.
func newCredentialsProvider(t http.RoundTripper) docker.CredentialsProvider { func newCredentialsProvider(t http.RoundTripper) docker.CredentialsProvider {
options := []creds.Opt{ options := []creds.Opt{
creds.WithPromptForCredentials(newPromptForCredentials()), creds.WithPromptForCredentials(newPromptForCredentials(os.Stdin, os.Stdout, os.Stderr)),
creds.WithPromptForCredentialStore(newPromptForCredentialStore()), creds.WithPromptForCredentialStore(newPromptForCredentialStore()),
creds.WithTransport(t), creds.WithTransport(t),
} }

View File

@ -1,11 +1,15 @@
package cmd package cmd
import ( import (
"bufio"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"golang.org/x/term"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
"github.com/ory/viper" "github.com/ory/viper"
@ -202,10 +206,18 @@ func runDeploy(cmd *cobra.Command, _ []string, newClient ClientFactory) (err err
return client.Deploy(cmd.Context(), config.Path) return client.Deploy(cmd.Context(), config.Path)
} }
func newPromptForCredentials() func(registry string) (docker.Credentials, error) { func newPromptForCredentials(in io.Reader, out, errOut io.Writer) func(registry string) (docker.Credentials, error) {
firstTime := true firstTime := true
return func(registry string) (docker.Credentials, error) { return func(registry string) (docker.Credentials, error) {
var result docker.Credentials var result docker.Credentials
if firstTime {
firstTime = false
fmt.Fprintf(out, "Please provide credentials for image registry (%s).\n", registry)
} else {
fmt.Fprintln(out, "Incorrect credentials, please try again.")
}
var qs = []*survey.Question{ var qs = []*survey.Question{
{ {
Name: "username", Name: "username",
@ -222,16 +234,42 @@ func newPromptForCredentials() func(registry string) (docker.Credentials, error)
Validate: survey.Required, Validate: survey.Required,
}, },
} }
if firstTime {
firstTime = false var (
fmt.Printf("Please provide credentials for image registry (%s).\n", registry) fr terminal.FileReader
ok bool
)
isTerm := false
if fr, ok = in.(terminal.FileReader); ok {
isTerm = term.IsTerminal(int(fr.Fd()))
}
if isTerm {
err := survey.Ask(qs, &result, survey.WithStdio(fr, out.(terminal.FileWriter), errOut))
if err != nil {
return docker.Credentials{}, err
}
} else { } else {
fmt.Println("Incorrect credentials, please try again.") reader := bufio.NewReader(in)
}
err := survey.Ask(qs, &result) fmt.Fprintf(out, "Username: ")
if err != nil { u, err := reader.ReadString('\n')
return docker.Credentials{}, err if err != nil {
return docker.Credentials{}, err
}
u = strings.Trim(u, "\r\n")
fmt.Fprintf(out, "Password: ")
p, err := reader.ReadString('\n')
if err != nil {
return docker.Credentials{}, err
}
p = strings.Trim(p, "\r\n")
result = docker.Credentials{Username: u, Password: p}
} }
return result, nil return result, nil
} }
} }

72
cmd/prompt_test.go Normal file
View File

@ -0,0 +1,72 @@
//go:build linux
// +build linux
package cmd
import (
"io"
"strings"
"testing"
"time"
"github.com/hinshun/vt10x"
"knative.dev/kn-plugin-func/docker"
)
func Test_newPromptForCredentials(t *testing.T) {
expectedCreds := docker.Credentials{
Username: "testuser",
Password: "testpwd",
}
console, _, err := vt10x.NewVT10XConsole()
if err != nil {
t.Fatal(err)
}
defer console.Close()
go func() {
_, _ = console.ExpectEOF()
}()
go func() {
chars := expectedCreds.Username + enter + expectedCreds.Password + enter
for _, ch := range chars {
_, _ = console.Send(string(ch))
time.Sleep(time.Millisecond * 50)
}
}()
tests := []struct {
name string
in io.Reader
out io.Writer
errOut io.Writer
}{
{
name: "with non-tty",
in: strings.NewReader(expectedCreds.Username + "\r\n" + expectedCreds.Password + "\r\n"),
out: io.Discard,
errOut: io.Discard,
},
{
name: "with tty",
in: console.Tty(),
out: console.Tty(),
errOut: console.Tty(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
credPrompt := newPromptForCredentials(tt.in, tt.out, tt.errOut)
cred, err := credPrompt("example.com")
if err != nil {
t.Fatal(err)
}
if cred != expectedCreds {
t.Errorf("bad credentials: %+v", cred)
}
})
}
}