Skip to content

Commit 58d318b

Browse files
authored
Merge pull request #6036 from thaJeztah/improve_username_handling
cli/command/registry: login: improve flag validation
2 parents 3707d07 + e7af181 commit 58d318b

File tree

2 files changed

+86
-5
lines changed

2 files changed

+86
-5
lines changed

cli/command/registry/login.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/docker/docker/registry"
2222
"github.com/pkg/errors"
2323
"github.com/spf13/cobra"
24+
"github.com/spf13/pflag"
2425
)
2526

2627
type loginOptions struct {
@@ -43,6 +44,9 @@ func NewLoginCommand(dockerCLI command.Cli) *cobra.Command {
4344
if len(args) > 0 {
4445
opts.serverAddress = args[0]
4546
}
47+
if err := verifyLoginFlags(cmd.Flags(), opts); err != nil {
48+
return err
49+
}
4650
return runLogin(cmd.Context(), dockerCLI, opts)
4751
},
4852
Annotations: map[string]string{
@@ -60,17 +64,35 @@ func NewLoginCommand(dockerCLI command.Cli) *cobra.Command {
6064
return cmd
6165
}
6266

63-
func verifyLoginOptions(dockerCLI command.Cli, opts *loginOptions) error {
67+
// verifyLoginFlags validates flags set on the command.
68+
//
69+
// TODO(thaJeztah); combine with verifyLoginOptions, but this requires rewrites of many tests.
70+
func verifyLoginFlags(flags *pflag.FlagSet, opts loginOptions) error {
71+
if flags.Changed("password-stdin") {
72+
if flags.Changed("password") {
73+
return errors.New("conflicting options: cannot specify both --password and --password-stdin")
74+
}
75+
if !flags.Changed("username") {
76+
return errors.New("the --password-stdin option requires --username to be set")
77+
}
78+
}
79+
if flags.Changed("username") && opts.user == "" {
80+
return errors.New("username is empty")
81+
}
82+
if flags.Changed("password") && opts.password == "" {
83+
return errors.New("password is empty")
84+
}
85+
return nil
86+
}
87+
88+
func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
6489
if opts.password != "" {
6590
_, _ = fmt.Fprintln(dockerCLI.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
66-
if opts.passwordStdin {
67-
return errors.New("--password and --password-stdin are mutually exclusive")
68-
}
6991
}
7092

7193
if opts.passwordStdin {
7294
if opts.user == "" {
73-
return errors.New("Must provide --username with --password-stdin")
95+
return errors.New("username is empty")
7496
}
7597

7698
contents, err := io.ReadAll(dockerCLI.In())

cli/command/registry/login_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"io"
78
"path/filepath"
89
"testing"
910
"time"
@@ -539,3 +540,61 @@ func TestIsOauthLoginDisabled(t *testing.T) {
539540
assert.Equal(t, disabled, tc.disabled)
540541
}
541542
}
543+
544+
func TestLoginValidateFlags(t *testing.T) {
545+
for _, tc := range []struct {
546+
name string
547+
args []string
548+
expectedErr string
549+
}{
550+
{
551+
name: "--password-stdin without --username",
552+
args: []string{"--password-stdin"},
553+
expectedErr: `the --password-stdin option requires --username to be set`,
554+
},
555+
{
556+
name: "--password-stdin with empty --username",
557+
args: []string{"--password-stdin", "--username", ""},
558+
expectedErr: `username is empty`,
559+
},
560+
{
561+
name: "empty --username",
562+
args: []string{"--username", ""},
563+
expectedErr: `username is empty`,
564+
},
565+
{
566+
name: "--username without value",
567+
args: []string{"--username"},
568+
expectedErr: `flag needs an argument: --username`,
569+
},
570+
{
571+
name: "conflicting options --password-stdin and --password",
572+
args: []string{"--password-stdin", "--password", ""},
573+
expectedErr: `conflicting options: cannot specify both --password and --password-stdin`,
574+
},
575+
{
576+
name: "empty --password",
577+
args: []string{"--password", ""},
578+
expectedErr: `password is empty`,
579+
},
580+
{
581+
name: "--password without value",
582+
args: []string{"--password"},
583+
expectedErr: `flag needs an argument: --password`,
584+
},
585+
} {
586+
t.Run(tc.name, func(t *testing.T) {
587+
cmd := NewLoginCommand(test.NewFakeCli(&fakeClient{}))
588+
cmd.SetOut(io.Discard)
589+
cmd.SetErr(io.Discard)
590+
cmd.SetArgs(tc.args)
591+
592+
err := cmd.Execute()
593+
if tc.expectedErr != "" {
594+
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
595+
} else {
596+
assert.Check(t, is.Nil(err))
597+
}
598+
})
599+
}
600+
}

0 commit comments

Comments
 (0)