login.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package client
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "runtime"
  8. "strings"
  9. Cli "github.com/docker/docker/cli"
  10. flag "github.com/docker/docker/pkg/mflag"
  11. "github.com/docker/docker/pkg/term"
  12. "github.com/docker/docker/registry"
  13. "github.com/docker/engine-api/client"
  14. "github.com/docker/engine-api/types"
  15. )
  16. // CmdLogin logs in or registers a user to a Docker registry service.
  17. //
  18. // If no server is specified, the user will be logged into or registered to the registry's index server.
  19. //
  20. // Usage: docker login SERVER
  21. func (cli *DockerCli) CmdLogin(args ...string) error {
  22. cmd := Cli.Subcmd("login", []string{"[SERVER]"}, Cli.DockerCommands["login"].Description+".\nIf no server is specified, the default is defined by the daemon.", true)
  23. cmd.Require(flag.Max, 1)
  24. flUser := cmd.String([]string{"u", "-username"}, "", "Username")
  25. flPassword := cmd.String([]string{"p", "-password"}, "", "Password")
  26. flEmail := cmd.String([]string{"e", "-email"}, "", "Email")
  27. cmd.ParseFlags(args, true)
  28. // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
  29. if runtime.GOOS == "windows" {
  30. cli.in = os.Stdin
  31. }
  32. // The daemon `/info` endpoint informs us of the default registry being
  33. // used. This is essential in cross-platforms environment, where for
  34. // example a Linux client might be interacting with a Windows daemon, hence
  35. // the default registry URL might be Windows specific.
  36. serverAddress := registry.IndexServer
  37. if info, err := cli.client.Info(); err != nil {
  38. fmt.Fprintf(cli.out, "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
  39. } else {
  40. serverAddress = info.IndexServerAddress
  41. }
  42. if len(cmd.Args()) > 0 {
  43. serverAddress = cmd.Arg(0)
  44. }
  45. authConfig, err := cli.configureAuth(*flUser, *flPassword, *flEmail, serverAddress)
  46. if err != nil {
  47. return err
  48. }
  49. response, err := cli.client.RegistryLogin(authConfig)
  50. if err != nil {
  51. if client.IsErrUnauthorized(err) {
  52. delete(cli.configFile.AuthConfigs, serverAddress)
  53. if err2 := cli.configFile.Save(); err2 != nil {
  54. fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2)
  55. }
  56. }
  57. return err
  58. }
  59. if err := cli.configFile.Save(); err != nil {
  60. return fmt.Errorf("Error saving config file: %v", err)
  61. }
  62. fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s\n", cli.configFile.Filename())
  63. if response.Status != "" {
  64. fmt.Fprintf(cli.out, "%s\n", response.Status)
  65. }
  66. return nil
  67. }
  68. func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
  69. if configDefault == "" {
  70. fmt.Fprintf(cli.out, "%s: ", prompt)
  71. } else {
  72. fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
  73. }
  74. }
  75. func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress string) (types.AuthConfig, error) {
  76. authconfig, ok := cli.configFile.AuthConfigs[serverAddress]
  77. if !ok {
  78. authconfig = types.AuthConfig{}
  79. }
  80. authconfig.Username = strings.TrimSpace(authconfig.Username)
  81. if flUser = strings.TrimSpace(flUser); flUser == "" {
  82. cli.promptWithDefault("Username", authconfig.Username)
  83. flUser = readInput(cli.in, cli.out)
  84. flUser = strings.TrimSpace(flUser)
  85. if flUser == "" {
  86. flUser = authconfig.Username
  87. }
  88. }
  89. if flUser == "" {
  90. return authconfig, fmt.Errorf("Error: Non-null Username Required")
  91. }
  92. if flPassword == "" {
  93. oldState, err := term.SaveState(cli.inFd)
  94. if err != nil {
  95. return authconfig, err
  96. }
  97. fmt.Fprintf(cli.out, "Password: ")
  98. term.DisableEcho(cli.inFd, oldState)
  99. flPassword = readInput(cli.in, cli.out)
  100. fmt.Fprint(cli.out, "\n")
  101. term.RestoreTerminal(cli.inFd, oldState)
  102. if flPassword == "" {
  103. return authconfig, fmt.Errorf("Error: Password Required")
  104. }
  105. }
  106. // Assume that a different username means they may not want to use
  107. // the email from the config file, so prompt it
  108. if flUser != authconfig.Username {
  109. if flEmail == "" {
  110. cli.promptWithDefault("Email", authconfig.Email)
  111. flEmail = readInput(cli.in, cli.out)
  112. if flEmail == "" {
  113. flEmail = authconfig.Email
  114. }
  115. }
  116. } else {
  117. // However, if they don't override the username use the
  118. // email from the cmd line if specified. IOW, allow
  119. // then to change/override them. And if not specified, just
  120. // use what's in the config file
  121. if flEmail == "" {
  122. flEmail = authconfig.Email
  123. }
  124. }
  125. authconfig.Username = flUser
  126. authconfig.Password = flPassword
  127. authconfig.Email = flEmail
  128. authconfig.ServerAddress = serverAddress
  129. cli.configFile.AuthConfigs[serverAddress] = authconfig
  130. return authconfig, nil
  131. }
  132. func readInput(in io.Reader, out io.Writer) string {
  133. reader := bufio.NewReader(in)
  134. line, _, err := reader.ReadLine()
  135. if err != nil {
  136. fmt.Fprintln(out, err.Error())
  137. os.Exit(1)
  138. }
  139. return string(line)
  140. }