chore: refactor password code for StdIO (#139)

This commit is contained in:
Dane Strandboge 2021-06-22 15:34:38 -05:00 committed by GitHub
parent f80b91730d
commit a3408e031a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 52 additions and 45 deletions

@ -1 +1 @@
Subproject commit d9aa0f1136d17956f45ebb63717220214933d477
Subproject commit 8a47036bed515857a28d7fba103c3b1b13ac64fd

View File

@ -8,8 +8,6 @@ import (
"github.com/influxdata/influx-cli/v2/pkg/stdio"
)
const MinPasswordLen = 8
// CLI is a container for common functionality used to execute commands.
type CLI struct {
StdIO stdio.StdIO

View File

@ -13,6 +13,7 @@ import (
"github.com/influxdata/influx-cli/v2/clients/bucket"
"github.com/influxdata/influx-cli/v2/config"
"github.com/influxdata/influx-cli/v2/internal/duration"
"github.com/influxdata/influx-cli/v2/pkg/stdio"
)
var (
@ -121,7 +122,7 @@ func (c Client) validateNoNameCollision(configName string) error {
// Unless the 'force' parameter is set, the user will be prompted to enter any missing information
// and to confirm the final request parameters.
func (c Client) onboardingRequest(params *Params) (req api.OnboardingRequest, err error) {
if (params.Force || params.Password != "") && len(params.Password) < clients.MinPasswordLen {
if (params.Force || params.Password != "") && len(params.Password) < stdio.MinPasswordLen {
return req, clients.ErrPasswordIsTooShort
}
@ -164,24 +165,11 @@ func (c Client) onboardingRequest(params *Params) (req api.OnboardingRequest, er
}
}
if params.Password == "" {
for {
pass1, err := c.StdIO.GetSecret("Please type your password", clients.MinPasswordLen)
if err != nil {
return req, err
}
// Don't bother with the length check the 2nd time, since we check equality to pass1.
pass2, err := c.StdIO.GetSecret("Please type your password again", 0)
if err != nil {
return req, err
}
if pass1 == pass2 {
req.Password = &pass1
break
}
if err := c.StdIO.Error("Passwords do not match"); err != nil {
return req, err
}
pass, err := c.StdIO.GetPassword("Please type your password")
if err != nil {
return req, err
}
req.Password = &pass
}
if params.Org == "" {
req.Org, err = c.StdIO.GetStringInput("Please type your primary organization name", "")

View File

@ -215,8 +215,7 @@ func Test_SetupSuccessInteractive(t *testing.T) {
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(bytesWritten.Write).AnyTimes()
stdio.EXPECT().Banner(gomock.Any())
stdio.EXPECT().GetStringInput(gomock.Eq("Please type your primary username"), gomock.Any()).Return(username, nil)
stdio.EXPECT().GetSecret(gomock.Eq("Please type your password"), gomock.Any()).Return(password, nil)
stdio.EXPECT().GetSecret(gomock.Eq("Please type your password again"), gomock.Any()).Return(password, nil)
stdio.EXPECT().GetPassword(gomock.Eq("Please type your password")).Return(password, nil)
stdio.EXPECT().GetStringInput(gomock.Eq("Please type your primary organization name"), gomock.Any()).Return(org, nil)
stdio.EXPECT().GetStringInput("Please type your primary bucket name", gomock.Any()).Return(bucket, nil)
stdio.EXPECT().GetStringInput("Please type your retention period in hours, or 0 for infinite", gomock.Any()).Return(strconv.Itoa(retentionHrs), nil)

View File

@ -8,6 +8,7 @@ import (
"github.com/influxdata/influx-cli/v2/api"
"github.com/influxdata/influx-cli/v2/clients"
"github.com/influxdata/influx-cli/v2/pkg/influxid"
"github.com/influxdata/influx-cli/v2/pkg/stdio"
)
type Client struct {
@ -49,7 +50,7 @@ func getOrgID(ctx context.Context, params *clients.OrgParams, c clients.CLI, org
}
func (c Client) Create(ctx context.Context, params *CreateParams) error {
if params.Password != "" && len(params.Password) < clients.MinPasswordLen {
if params.Password != "" && len(params.Password) < stdio.MinPasswordLen {
return clients.ErrPasswordIsTooShort
}
@ -159,31 +160,16 @@ func (c Client) SetPassword(ctx context.Context, params *SetPasswordParams) erro
id = (*users.Users)[0].GetId()
}
var password string
for {
pass1, err := c.StdIO.GetSecret(fmt.Sprintf("Please type new password for %q", displayName), clients.MinPasswordLen)
if err != nil {
return err
}
// Don't bother with the length check the 2nd time, since we check equality to pass1.
pass2, err := c.StdIO.GetSecret("Please type new password again", 0)
if err != nil {
return err
}
if pass1 == pass2 {
password = pass1
break
}
if err := c.StdIO.Error("Passwords do not match"); err != nil {
return err
}
password, err := c.StdIO.GetPassword(fmt.Sprintf("Please type new password for %q", displayName))
if err != nil {
return err
}
body := api.PasswordResetBody{Password: password}
if err := c.PostUsersIDPassword(ctx, id).PasswordResetBody(body).Execute(); err != nil {
return fmt.Errorf("failed to set password for user %q: %w", params.Id.String(), err)
}
_, err := c.StdIO.Write([]byte(fmt.Sprintf("Successfully updated password for user %q\n", displayName)))
_, err = c.StdIO.Write([]byte(fmt.Sprintf("Successfully updated password for user %q\n", displayName)))
return err
}

View File

@ -573,7 +573,7 @@ func TestClient_SetPassword(t *testing.T) {
stdio := mock.NewMockStdIO(ctrl)
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(stdout.Write).AnyTimes()
if !tc.noExpectAsk {
stdio.EXPECT().GetSecret(gomock.Any(), gomock.Any()).Return("mypassword", nil).Times(2)
stdio.EXPECT().GetPassword(gomock.Any()).Return("mypassword", nil)
}
cli := user.Client{CLI: clients.CLI{StdIO: stdio}, UsersApi: userApi}

View File

@ -75,6 +75,21 @@ func (mr *MockStdIOMockRecorder) GetConfirm(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfirm", reflect.TypeOf((*MockStdIO)(nil).GetConfirm), arg0)
}
// GetPassword mocks base method.
func (m *MockStdIO) GetPassword(arg0 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPassword", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPassword indicates an expected call of GetPassword.
func (mr *MockStdIOMockRecorder) GetPassword(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPassword", reflect.TypeOf((*MockStdIO)(nil).GetPassword), arg0)
}
// GetSecret mocks base method.
func (m *MockStdIO) GetSecret(arg0 string, arg1 int) (string, error) {
m.ctrl.T.Helper()

View File

@ -67,7 +67,7 @@ func (t *terminalStdio) GetStringInput(prompt, defaultValue string) (input strin
return
}
// GetSecret prompts the user for a password.
// GetSecret prompts the user for a secret.
func (t *terminalStdio) GetSecret(prompt string, minLen int) (password string, err error) {
question := survey.Password{Message: prompt}
opts := []survey.AskOpt{survey.WithStdio(t.Stdin, t.Stdout, t.Stderr)}
@ -79,6 +79,24 @@ func (t *terminalStdio) GetSecret(prompt string, minLen int) (password string, e
return
}
// GetPassword prompts the user for a secret twice, and inputs must match.
// Uses stdio.MinPasswordLen as the minimum input length
func (t *terminalStdio) GetPassword(prompt string) (string, error) {
pass1, err := t.GetSecret(prompt, MinPasswordLen)
if err != nil {
return "", err
}
// Don't bother with the length check the 2nd time, since we check equality to pass1.
pass2, err := t.GetSecret(prompt+" again", 0)
if err != nil {
return "", err
}
if pass1 == pass2 {
return pass1, nil
}
return "", t.Error("Passwords do not match")
}
// GetConfirm asks the user for a y/n answer to a prompt.
func (t *terminalStdio) GetConfirm(prompt string) (answer bool) {
question := survey.Confirm{

View File

@ -2,6 +2,8 @@ package stdio
import "io"
const MinPasswordLen = 8
type StdIO interface {
io.Writer
WriteErr(p []byte) (n int, err error)
@ -9,5 +11,6 @@ type StdIO interface {
Error(message string) error
GetStringInput(prompt, defaultValue string) (string, error)
GetSecret(prompt string, minLen int) (string, error)
GetPassword(prompt string) (string, error)
GetConfirm(prompt string) bool
}