diff --git a/api/client/login.go b/api/client/login.go index 0d0588b389..470cb5780c 100644 --- a/api/client/login.go +++ b/api/client/login.go @@ -17,7 +17,7 @@ import ( "github.com/docker/engine-api/types" ) -// CmdLogin logs in or registers a user to a Docker registry service. +// CmdLogin logs in a user to a Docker registry service. // // If no server is specified, the user will be logged into or registered to the registry's index server. // @@ -28,7 +28,9 @@ func (cli *DockerCli) CmdLogin(args ...string) error { flUser := cmd.String([]string{"u", "-username"}, "", "Username") flPassword := cmd.String([]string{"p", "-password"}, "", "Password") - flEmail := cmd.String([]string{"e", "-email"}, "", "Email") + + // Deprecated in 1.11: Should be removed in docker 1.13 + cmd.String([]string{"#e", "#-email"}, "", "Email") cmd.ParseFlags(args, true) @@ -38,13 +40,15 @@ func (cli *DockerCli) CmdLogin(args ...string) error { } var serverAddress string + var isDefaultRegistry bool if len(cmd.Args()) > 0 { serverAddress = cmd.Arg(0) } else { serverAddress = cli.electAuthServer() + isDefaultRegistry = true } - authConfig, err := cli.configureAuth(*flUser, *flPassword, *flEmail, serverAddress) + authConfig, err := cli.configureAuth(*flUser, *flPassword, serverAddress, isDefaultRegistry) if err != nil { return err } @@ -77,7 +81,7 @@ func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) { } } -func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress string) (types.AuthConfig, error) { +func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) { authconfig, err := getCredentials(cli.configFile, serverAddress) if err != nil { return authconfig, err @@ -86,6 +90,10 @@ func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress s authconfig.Username = strings.TrimSpace(authconfig.Username) if flUser = strings.TrimSpace(flUser); flUser == "" { + if isDefaultRegistry { + // if this is a defauly registry (docker hub), then display the following message. + fmt.Fprintln(cli.out, "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.") + } cli.promptWithDefault("Username", authconfig.Username) flUser = readInput(cli.in, cli.out) flUser = strings.TrimSpace(flUser) @@ -115,29 +123,8 @@ func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress s } } - // Assume that a different username means they may not want to use - // the email from the config file, so prompt it - if flUser != authconfig.Username { - if flEmail == "" { - cli.promptWithDefault("Email", authconfig.Email) - flEmail = readInput(cli.in, cli.out) - if flEmail == "" { - flEmail = authconfig.Email - } - } - } else { - // However, if they don't override the username use the - // email from the cmd line if specified. IOW, allow - // then to change/override them. And if not specified, just - // use what's in the config file - if flEmail == "" { - flEmail = authconfig.Email - } - } - authconfig.Username = flUser authconfig.Password = flPassword - authconfig.Email = flEmail authconfig.ServerAddress = serverAddress return authconfig, nil diff --git a/api/client/utils.go b/api/client/utils.go index 73dbb673b7..a3500319a4 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -48,7 +48,7 @@ func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes. return func() (string, error) { fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName) indexServer := registry.GetAuthConfigKey(index) - authConfig, err := cli.configureAuth("", "", "", indexServer) + authConfig, err := cli.configureAuth("", "", indexServer, false) if err != nil { return "", err } diff --git a/cliconfig/config.go b/cliconfig/config.go index 86d08be2df..54e0ea387d 100644 --- a/cliconfig/config.go +++ b/cliconfig/config.go @@ -88,11 +88,6 @@ func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error { if err != nil { return err } - origEmail := strings.Split(arr[1], " = ") - if len(origEmail) != 2 { - return fmt.Errorf("Invalid Auth config file") - } - authConfig.Email = origEmail[1] authConfig.ServerAddress = defaultIndexserver configFile.AuthConfigs[defaultIndexserver] = authConfig } else { diff --git a/cliconfig/config_test.go b/cliconfig/config_test.go index 17f9fd673b..5ea6f4071c 100644 --- a/cliconfig/config_test.go +++ b/cliconfig/config_test.go @@ -111,12 +111,9 @@ func TestOldInvalidsAuth(t *testing.T) { invalids := map[string]string{ `username = test`: "The Auth config file is empty", `username -password -email`: "Invalid Auth config file", +password`: "Invalid Auth config file", `username = test email`: "Invalid auth configuration file", - `username = am9lam9lOmhlbGxv -email`: "Invalid Auth config file", } tmpHome, err := ioutil.TempDir("", "config-test") @@ -164,7 +161,7 @@ func TestOldValidAuth(t *testing.T) { fn := filepath.Join(tmpHome, oldConfigfile) js := `username = am9lam9lOmhlbGxv -email = user@example.com` + email = user@example.com` if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil { t.Fatal(err) } @@ -176,15 +173,23 @@ email = user@example.com` // defaultIndexserver is https://index.docker.io/v1/ ac := config.AuthConfigs["https://index.docker.io/v1/"] - if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" { + if ac.Username != "joejoe" || ac.Password != "hello" { t.Fatalf("Missing data from parsing:\n%q", config) } // Now save it and make sure it shows up in new form configStr := saveConfigAndValidateNewFormat(t, config, tmpHome) - if !strings.Contains(configStr, "user@example.com") { - t.Fatalf("Should have save in new form: %s", configStr) + expConfStr := `{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "am9lam9lOmhlbGxv" + } + } +}` + + if configStr != expConfStr { + t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr) } } @@ -239,15 +244,24 @@ func TestOldJson(t *testing.T) { } ac := config.AuthConfigs["https://index.docker.io/v1/"] - if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" { + if ac.Username != "joejoe" || ac.Password != "hello" { t.Fatalf("Missing data from parsing:\n%q", config) } // Now save it and make sure it shows up in new form configStr := saveConfigAndValidateNewFormat(t, config, tmpHome) - if !strings.Contains(configStr, "user@example.com") { - t.Fatalf("Should have save in new form: %s", configStr) + expConfStr := `{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "am9lam9lOmhlbGxv", + "email": "user@example.com" + } + } +}` + + if configStr != expConfStr { + t.Fatalf("Should have save in new form: \n'%s'\n not \n'%s'\n", configStr, expConfStr) } } @@ -259,7 +273,7 @@ func TestNewJson(t *testing.T) { defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) - js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }` + js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }` if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil { t.Fatal(err) } @@ -270,15 +284,62 @@ func TestNewJson(t *testing.T) { } ac := config.AuthConfigs["https://index.docker.io/v1/"] - if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" { + if ac.Username != "joejoe" || ac.Password != "hello" { t.Fatalf("Missing data from parsing:\n%q", config) } // Now save it and make sure it shows up in new form configStr := saveConfigAndValidateNewFormat(t, config, tmpHome) - if !strings.Contains(configStr, "user@example.com") { - t.Fatalf("Should have save in new form: %s", configStr) + expConfStr := `{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "am9lam9lOmhlbGxv" + } + } +}` + + if configStr != expConfStr { + t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr) + } +} + +func TestNewJsonNoEmail(t *testing.T) { + tmpHome, err := ioutil.TempDir("", "config-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpHome) + + fn := filepath.Join(tmpHome, ConfigFileName) + js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }` + if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil { + t.Fatal(err) + } + + config, err := Load(tmpHome) + if err != nil { + t.Fatalf("Failed loading on empty json file: %q", err) + } + + ac := config.AuthConfigs["https://index.docker.io/v1/"] + if ac.Username != "joejoe" || ac.Password != "hello" { + t.Fatalf("Missing data from parsing:\n%q", config) + } + + // Now save it and make sure it shows up in new form + configStr := saveConfigAndValidateNewFormat(t, config, tmpHome) + + expConfStr := `{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "am9lam9lOmhlbGxv" + } + } +}` + + if configStr != expConfStr { + t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr) } } @@ -366,7 +427,7 @@ func TestJsonReaderNoFile(t *testing.T) { } ac := config.AuthConfigs["https://index.docker.io/v1/"] - if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" { + if ac.Username != "joejoe" || ac.Password != "hello" { t.Fatalf("Missing data from parsing:\n%q", config) } @@ -381,7 +442,7 @@ func TestOldJsonReaderNoFile(t *testing.T) { } ac := config.AuthConfigs["https://index.docker.io/v1/"] - if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" { + if ac.Username != "joejoe" || ac.Password != "hello" { t.Fatalf("Missing data from parsing:\n%q", config) } } @@ -404,7 +465,7 @@ func TestJsonWithPsFormatNoFile(t *testing.T) { func TestJsonSaveWithNoFile(t *testing.T) { js := `{ - "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } }, + "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } }, "psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}" }` config, err := LoadFromReader(strings.NewReader(js)) @@ -426,9 +487,16 @@ func TestJsonSaveWithNoFile(t *testing.T) { t.Fatalf("Failed saving to file: %q", err) } buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName)) - if !strings.Contains(string(buf), `"auths":`) || - !strings.Contains(string(buf), "user@example.com") { - t.Fatalf("Should have save in new form: %s", string(buf)) + expConfStr := `{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "am9lam9lOmhlbGxv" + } + }, + "psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}" +}` + if string(buf) != expConfStr { + t.Fatalf("Should have save in new form: \n%s\nnot \n%s", string(buf), expConfStr) } } @@ -454,14 +522,23 @@ func TestLegacyJsonSaveWithNoFile(t *testing.T) { t.Fatalf("Failed saving to file: %q", err) } buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName)) - if !strings.Contains(string(buf), `"auths":`) || - !strings.Contains(string(buf), "user@example.com") { - t.Fatalf("Should have save in new form: %s", string(buf)) + + expConfStr := `{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "am9lam9lOmhlbGxv", + "email": "user@example.com" + } + } +}` + + if string(buf) != expConfStr { + t.Fatalf("Should have save in new form: \n%s\n not \n%s", string(buf), expConfStr) } } func TestEncodeAuth(t *testing.T) { - newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"} + newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"} authStr := encodeAuth(newAuthConfig) decAuthConfig := &types.AuthConfig{} var err error diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 0c98b4d801..23e1dfe268 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -811,7 +811,7 @@ _docker_daemon() { return ;; esac - + local key=$(__docker_map_key_of_current_option '--storage-opt') case "$key" in dm.@(blkdiscard|override_udev_sync_check|use_deferred_@(removal|deletion))) @@ -1205,14 +1205,14 @@ _docker_load() { _docker_login() { case "$prev" in - --email|-e|--password|-p|--username|-u) + --password|-p|--username|-u) return ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--email -e --help --password -p --username -u" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--help --password -p --username -u" -- "$cur" ) ) ;; esac } diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index 4e1c1cf408..1e734c7d3a 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -221,8 +221,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from load' -l help -d 'Print complete -c docker -A -f -n '__fish_seen_subcommand_from load' -s i -l input -d 'Read from a tar archive file, instead of STDIN' # login -complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Register or log in to a Docker registry server' -complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s e -l email -d 'Email' +complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Log in to a Docker registry server' complete -c docker -A -f -n '__fish_seen_subcommand_from login' -l help -d 'Print usage' complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password' complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'Username' @@ -399,5 +398,3 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a version -d 'Show the D complete -c docker -f -n '__fish_docker_no_subcommand' -a wait -d 'Block until a container stops, then print its exit code' complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -l help -d 'Print usage' complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -a '(__fish_print_docker_containers running)' -d "Container" - - diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 4e6a18f11b..047e2189f6 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -812,7 +812,6 @@ __docker_subcommand() { (login) _arguments $(__docker_arguments) \ $opts_help \ - "($help -e --email)"{-e=,--email=}"[Email]:email: " \ "($help -p --password)"{-p=,--password=}"[Password]:password: " \ "($help -u --user)"{-u=,--user=}"[Username]:username: " \ "($help -)1:server: " && ret=0 diff --git a/docs/deprecated.md b/docs/deprecated.md index 4ed127f953..7a351ac176 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -14,6 +14,13 @@ weight=80 The following list of features are deprecated in Engine. +### `-e` and `--email` flags on `docker login` +**Deprecated In Release: v1.11** + +**Target For Removal In Release: v1.13** + +The docker login command is removing the ability to automatically register for an account with the target registry if the given username doesn't exist. Due to this change, the email flag is no longer required, and will be deprecated. + ### Ambiguous event fields in API **Deprecated In Release: v1.10** diff --git a/docs/reference/commandline/login.md b/docs/reference/commandline/login.md index b20fb6cd96..832902984c 100644 --- a/docs/reference/commandline/login.md +++ b/docs/reference/commandline/login.md @@ -12,10 +12,9 @@ parent = "smn_cli" Usage: docker login [OPTIONS] [SERVER] - Register or log in to a Docker registry server, if no server is + Log in to a Docker registry server, if no server is specified "https://index.docker.io/v1/" is the default. - -e, --email="" Email --help Print usage -p, --password="" Password -u, --username="" Username @@ -27,10 +26,10 @@ adding the server name. $ docker login localhost:8080 -`docker login` requires user to use `sudo` or be `root`, except when: +`docker login` requires user to use `sudo` or be `root`, except when: 1. connecting to a remote daemon, such as a `docker-machine` provisioned `docker engine`. -2. user is added to the `docker` group. This will impact the security of your system; the `docker` group is `root` equivalent. See [Docker Daemon Attack Surface](https://docs.docker.com/security/security/#docker-daemon-attack-surface) for details. +2. user is added to the `docker` group. This will impact the security of your system; the `docker` group is `root` equivalent. See [Docker Daemon Attack Surface](https://docs.docker.com/security/security/#docker-daemon-attack-surface) for details. You can log into any public or private repository for which you have credentials. When you log in, the command stores encoded credentials in diff --git a/docs/userguide/containers/dockerrepos.md b/docs/userguide/containers/dockerrepos.md index 257f87635b..b0c6fb7c69 100644 --- a/docs/userguide/containers/dockerrepos.md +++ b/docs/userguide/containers/dockerrepos.md @@ -33,15 +33,11 @@ Docker itself provides access to Docker Hub services via the `docker search`, ### Account creation and login Typically, you'll want to start by creating an account on Docker Hub (if you haven't already) and logging in. You can create your account directly on -[Docker Hub](https://hub.docker.com/account/signup/), or by running: +[Docker Hub](https://hub.docker.com/account/signup/). $ docker login -This will prompt you for a user name, which will become the public namespace for your -public repositories. -If your user name is available, Docker will prompt you to enter a password and your -e-mail address. It will then automatically log you in. You can now commit and -push your own images up to your repos on Docker Hub. +You can now commit and push your own images up to your repos on Docker Hub. > **Note:** > Your authentication credentials will be stored in the `~/.docker/config.json` diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index fc27a0b22f..2f19508930 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -6548,7 +6548,7 @@ func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) { } func (s *DockerRegistryAuthSuite) TestBuildFromAuthenticatedRegistry(c *check.C) { - dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL) + dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) baseImage := privateRegistryURL + "/baseimage" diff --git a/integration-cli/docker_cli_login_test.go b/integration-cli/docker_cli_login_test.go index ab6294092d..204d032e10 100644 --- a/integration-cli/docker_cli_login_test.go +++ b/integration-cli/docker_cli_login_test.go @@ -20,11 +20,25 @@ func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) { } func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistry(c *check.C) { + // wrong credentials + out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", privateRegistryURL) + c.Assert(err, checker.NotNil, check.Commentf(out)) + c.Assert(out, checker.Contains, "401 Unauthorized") + + // now it's fine + dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL) +} + +func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistryDeprecatedEmailFlag(c *check.C) { + // Test to make sure login still works with the deprecated -e and --email flags // wrong credentials out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", "-e", s.reg.email, privateRegistryURL) c.Assert(err, checker.NotNil, check.Commentf(out)) c.Assert(out, checker.Contains, "401 Unauthorized") // now it's fine + // -e flag dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL) + // --email flag + dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "--email", s.reg.email, privateRegistryURL) } diff --git a/integration-cli/docker_cli_pull_local_test.go b/integration-cli/docker_cli_pull_local_test.go index 96388b18e8..e032abf6bd 100644 --- a/integration-cli/docker_cli_pull_local_test.go +++ b/integration-cli/docker_cli_pull_local_test.go @@ -390,7 +390,6 @@ func (s *DockerRegistryAuthSuite) TestPullWithExternalAuth(c *check.C) { b, err := ioutil.ReadFile(configPath) c.Assert(err, checker.IsNil) c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":") - c.Assert(string(b), checker.Contains, "email") dockerCmd(c, "--config", tmp, "tag", "busybox", repoName) dockerCmd(c, "--config", tmp, "push", repoName) diff --git a/integration-cli/docker_cli_v2_only_test.go b/integration-cli/docker_cli_v2_only_test.go index b0bfb6f424..0f640add31 100644 --- a/integration-cli/docker_cli_v2_only_test.go +++ b/integration-cli/docker_cli_v2_only_test.go @@ -112,7 +112,7 @@ func (s *DockerRegistrySuite) TestV1(c *check.C) { s.d.Cmd("run", repoName) c.Assert(v1Repo, check.Not(check.Equals), 1, check.Commentf("Expected v1 repository access after run")) - s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport) + s.d.Cmd("login", "-u", "richard", "-p", "testtest", reg.hostport) c.Assert(v1Logins, check.Not(check.Equals), 0, check.Commentf("Expected v1 login attempt")) s.d.Cmd("tag", "busybox", repoName) diff --git a/man/docker-login.1.md b/man/docker-login.1.md index c32a49b075..ea89937644 100644 --- a/man/docker-login.1.md +++ b/man/docker-login.1.md @@ -2,26 +2,25 @@ % Docker Community % JUNE 2014 # NAME -docker-login - Register or log in to a Docker registry. +docker-login - Log in to a Docker registry. # SYNOPSIS **docker login** -[**-e**|**--email**[=*EMAIL*]] [**--help**] [**-p**|**--password**[=*PASSWORD*]] [**-u**|**--username**[=*USERNAME*]] [SERVER] # DESCRIPTION -Register or log in to a Docker Registry located on the specified +Log in to a Docker Registry located on the specified `SERVER`. You can specify a URL or a `hostname` for the `SERVER` value. If you do not specify a `SERVER`, the command uses Docker's public registry located at `https://registry-1.docker.io/` by default. To get a username/password for Docker's public registry, create an account on Docker Hub. -`docker login` requires user to use `sudo` or be `root`, except when: +`docker login` requires user to use `sudo` or be `root`, except when: 1. connecting to a remote daemon, such as a `docker-machine` provisioned `docker engine`. -2. user is added to the `docker` group. This will impact the security of your system; the `docker` group is `root` equivalent. See [Docker Daemon Attack Surface](https://docs.docker.com/articles/security/#docker-daemon-attack-surface) for details. +2. user is added to the `docker` group. This will impact the security of your system; the `docker` group is `root` equivalent. See [Docker Daemon Attack Surface](https://docs.docker.com/articles/security/#docker-daemon-attack-surface) for details. You can log into any public or private repository for which you have credentials. When you log in, the command stores encoded credentials in @@ -31,9 +30,6 @@ credentials. When you log in, the command stores encoded credentials in > # OPTIONS -**-e**, **--email**="" - Email - **--help** Print usage statement diff --git a/registry/auth.go b/registry/auth.go index 7175598c71..bd7bd52dde 100644 --- a/registry/auth.go +++ b/registry/auth.go @@ -1,7 +1,6 @@ package registry import ( - "encoding/json" "fmt" "io/ioutil" "net/http" @@ -24,11 +23,8 @@ func Login(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, er // loginV1 tries to register/login to the v1 registry server. func loginV1(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, error) { var ( - status string - respBody []byte - err error - respStatusCode = 0 - serverAddress = authConfig.ServerAddress + err error + serverAddress = authConfig.ServerAddress ) logrus.Debugf("attempting v1 login to registry endpoint %s", registryEndpoint) @@ -39,93 +35,37 @@ func loginV1(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, loginAgainstOfficialIndex := serverAddress == IndexServer - // to avoid sending the server address to the server it should be removed before being marshaled - authCopy := *authConfig - authCopy.ServerAddress = "" - - jsonBody, err := json.Marshal(authCopy) + req, err := http.NewRequest("GET", serverAddress+"users/", nil) + req.SetBasicAuth(authConfig.Username, authConfig.Password) + resp, err := registryEndpoint.client.Do(req) if err != nil { - return "", fmt.Errorf("Config Error: %s", err) + return "", err } - - // using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status. - b := strings.NewReader(string(jsonBody)) - resp1, err := registryEndpoint.client.Post(serverAddress+"users/", "application/json; charset=utf-8", b) + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) if err != nil { - return "", fmt.Errorf("Server Error: %s", err) + return "", err } - defer resp1.Body.Close() - respStatusCode = resp1.StatusCode - respBody, err = ioutil.ReadAll(resp1.Body) - if err != nil { - return "", fmt.Errorf("Server Error: [%#v] %s", respStatusCode, err) - } - - if respStatusCode == 201 { + if resp.StatusCode == http.StatusOK { + return "Login Succeeded", nil + } else if resp.StatusCode == http.StatusUnauthorized { if loginAgainstOfficialIndex { - status = "Account created. Please use the confirmation link we sent" + - " to your e-mail to activate it." - } else { - // *TODO: Use registry configuration to determine what this says, if anything? - status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it." + return "", fmt.Errorf("Wrong login/password, please try again. Haven't got a Docker ID? Create one at https://hub.docker.com") } - } else if respStatusCode == 400 { - if string(respBody) == "\"Username or email already exists\"" { - req, err := http.NewRequest("GET", serverAddress+"users/", nil) - req.SetBasicAuth(authConfig.Username, authConfig.Password) - resp, err := registryEndpoint.client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - if resp.StatusCode == 200 { - return "Login Succeeded", nil - } else if resp.StatusCode == 401 { - return "", fmt.Errorf("Wrong login/password, please try again") - } else if resp.StatusCode == 403 { - if loginAgainstOfficialIndex { - return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.") - } - // *TODO: Use registry configuration to determine what this says, if anything? - return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress) - } else if resp.StatusCode == 500 { // Issue #14326 - logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body) - return "", fmt.Errorf("Internal Server Error") - } - return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header) - } - return "", fmt.Errorf("Registration: %s", respBody) - - } else if respStatusCode == 401 { - // This case would happen with private registries where /v1/users is - // protected, so people can use `docker login` as an auth check. - req, err := http.NewRequest("GET", serverAddress+"users/", nil) - req.SetBasicAuth(authConfig.Username, authConfig.Password) - resp, err := registryEndpoint.client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - if resp.StatusCode == 200 { - return "Login Succeeded", nil - } else if resp.StatusCode == 401 { - return "", fmt.Errorf("Wrong login/password, please try again") - } else { - return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, - resp.StatusCode, resp.Header) + return "", fmt.Errorf("Wrong login/password, please try again") + } else if resp.StatusCode == http.StatusForbidden { + if loginAgainstOfficialIndex { + return "", fmt.Errorf("Login: Account is not active. Please check your e-mail for a confirmation link.") } + // *TODO: Use registry configuration to determine what this says, if anything? + return "", fmt.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress) + } else if resp.StatusCode == http.StatusInternalServerError { // Issue #14326 + logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body) + return "", fmt.Errorf("Internal Server Error") } else { - return "", fmt.Errorf("Unexpected status code [%d] : %s", respStatusCode, respBody) + return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, + resp.StatusCode, resp.Header) } - return status, nil } // loginV2 tries to login to the v2 registry server. The given registry endpoint has been diff --git a/registry/auth_test.go b/registry/auth_test.go index caff8667d1..eedee44ef7 100644 --- a/registry/auth_test.go +++ b/registry/auth_test.go @@ -14,7 +14,6 @@ func buildAuthConfigs() map[string]types.AuthConfig { authConfigs[registry] = types.AuthConfig{ Username: "docker-user", Password: "docker-pass", - Email: "docker@docker.io", } } @@ -30,9 +29,6 @@ func TestSameAuthDataPostSave(t *testing.T) { if authConfig.Password != "docker-pass" { t.Fail() } - if authConfig.Email != "docker@docker.io" { - t.Fail() - } if authConfig.Auth != "" { t.Fail() } @@ -62,17 +58,14 @@ func TestResolveAuthConfigFullURL(t *testing.T) { registryAuth := types.AuthConfig{ Username: "foo-user", Password: "foo-pass", - Email: "foo@example.com", } localAuth := types.AuthConfig{ Username: "bar-user", Password: "bar-pass", - Email: "bar@example.com", } officialAuth := types.AuthConfig{ Username: "baz-user", Password: "baz-pass", - Email: "baz@example.com", } authConfigs[IndexServer] = officialAuth @@ -105,7 +98,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) { for configKey, registries := range validRegistries { configured, ok := expectedAuths[configKey] - if !ok || configured.Email == "" { + if !ok { t.Fail() } index := ®istrytypes.IndexInfo{ @@ -114,13 +107,13 @@ func TestResolveAuthConfigFullURL(t *testing.T) { for _, registry := range registries { authConfigs[registry] = configured resolved := ResolveAuthConfig(authConfigs, index) - if resolved.Email != configured.Email { - t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email) + if resolved.Username != configured.Username || resolved.Password != configured.Password { + t.Errorf("%s -> %v != %v\n", registry, resolved, configured) } delete(authConfigs, registry) resolved = ResolveAuthConfig(authConfigs, index) - if resolved.Email == configured.Email { - t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email) + if resolved.Username == configured.Username || resolved.Password == configured.Password { + t.Errorf("%s -> %v == %v\n", registry, resolved, configured) } } } diff --git a/registry/session.go b/registry/session.go index 4b18d0d1a1..daf4498209 100644 --- a/registry/session.go +++ b/registry/session.go @@ -752,7 +752,6 @@ func (r *Session) GetAuthConfig(withPasswd bool) *types.AuthConfig { return &types.AuthConfig{ Username: r.authConfig.Username, Password: password, - Email: r.authConfig.Email, } }