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.
func newCredentialsProvider(t http.RoundTripper) docker.CredentialsProvider {
options := []creds.Opt{
creds.WithPromptForCredentials(newPromptForCredentials()),
creds.WithPromptForCredentials(newPromptForCredentials(os.Stdin, os.Stdout, os.Stderr)),
creds.WithPromptForCredentialStore(newPromptForCredentialStore()),
creds.WithTransport(t),
}

View File

@ -1,11 +1,15 @@
package cmd
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"strings"
"golang.org/x/term"
"github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal"
"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)
}
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
return func(registry string) (docker.Credentials, error) {
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{
{
Name: "username",
@ -222,16 +234,42 @@ func newPromptForCredentials() func(registry string) (docker.Credentials, error)
Validate: survey.Required,
},
}
if firstTime {
firstTime = false
fmt.Printf("Please provide credentials for image registry (%s).\n", registry)
var (
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 {
fmt.Println("Incorrect credentials, please try again.")
}
err := survey.Ask(qs, &result)
if err != nil {
return docker.Credentials{}, err
reader := bufio.NewReader(in)
fmt.Fprintf(out, "Username: ")
u, err := reader.ReadString('\n')
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
}
}

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)
}
})
}
}