login.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package client
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "runtime"
  8. "strings"
  9. "golang.org/x/net/context"
  10. Cli "github.com/docker/docker/cli"
  11. "github.com/docker/docker/cliconfig/configfile"
  12. "github.com/docker/docker/cliconfig/credentials"
  13. flag "github.com/docker/docker/pkg/mflag"
  14. "github.com/docker/docker/pkg/term"
  15. "github.com/docker/engine-api/types"
  16. )
  17. // CmdLogin logs in a user to a Docker registry service.
  18. //
  19. // If no server is specified, the user will be logged into or registered to the registry's index server.
  20. //
  21. // Usage: docker login SERVER
  22. func (cli *DockerCli) CmdLogin(args ...string) error {
  23. cmd := Cli.Subcmd("login", []string{"[SERVER]"}, Cli.DockerCommands["login"].Description+".\nIf no server is specified, the default is defined by the daemon.", true)
  24. cmd.Require(flag.Max, 1)
  25. flUser := cmd.String([]string{"u", "-username"}, "", "Username")
  26. flPassword := cmd.String([]string{"p", "-password"}, "", "Password")
  27. // Deprecated in 1.11: Should be removed in docker 1.13
  28. cmd.String([]string{"#e", "#-email"}, "", "Email")
  29. cmd.ParseFlags(args, true)
  30. // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
  31. if runtime.GOOS == "windows" {
  32. cli.in = os.Stdin
  33. }
  34. ctx := context.Background()
  35. var serverAddress string
  36. var isDefaultRegistry bool
  37. if len(cmd.Args()) > 0 {
  38. serverAddress = cmd.Arg(0)
  39. } else {
  40. serverAddress = cli.electAuthServer(ctx)
  41. isDefaultRegistry = true
  42. }
  43. authConfig, err := cli.configureAuth(*flUser, *flPassword, serverAddress, isDefaultRegistry)
  44. if err != nil {
  45. return err
  46. }
  47. response, err := cli.client.RegistryLogin(ctx, authConfig)
  48. if err != nil {
  49. return err
  50. }
  51. if response.IdentityToken != "" {
  52. authConfig.Password = ""
  53. authConfig.IdentityToken = response.IdentityToken
  54. }
  55. if err := storeCredentials(cli.configFile, authConfig); err != nil {
  56. return fmt.Errorf("Error saving credentials: %v", err)
  57. }
  58. if response.Status != "" {
  59. fmt.Fprintln(cli.out, response.Status)
  60. }
  61. return nil
  62. }
  63. func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
  64. if configDefault == "" {
  65. fmt.Fprintf(cli.out, "%s: ", prompt)
  66. } else {
  67. fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
  68. }
  69. }
  70. func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
  71. authconfig, err := getCredentials(cli.configFile, serverAddress)
  72. if err != nil {
  73. return authconfig, err
  74. }
  75. // Some links documenting this:
  76. // - https://code.google.com/archive/p/mintty/issues/56
  77. // - https://github.com/docker/docker/issues/15272
  78. // - https://mintty.github.io/ (compatibility)
  79. // Linux will hit this if you attempt `cat | docker login`, and Windows
  80. // will hit this if you attempt docker login from mintty where stdin
  81. // is a pipe, not a character based console.
  82. if flPassword == "" && !cli.isTerminalIn {
  83. return authconfig, fmt.Errorf("Error: Cannot perform an interactive logon from a non TTY device")
  84. }
  85. authconfig.Username = strings.TrimSpace(authconfig.Username)
  86. if flUser = strings.TrimSpace(flUser); flUser == "" {
  87. if isDefaultRegistry {
  88. // if this is a defauly registry (docker hub), then display the following message.
  89. 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.")
  90. }
  91. cli.promptWithDefault("Username", authconfig.Username)
  92. flUser = readInput(cli.in, cli.out)
  93. flUser = strings.TrimSpace(flUser)
  94. if flUser == "" {
  95. flUser = authconfig.Username
  96. }
  97. }
  98. if flUser == "" {
  99. return authconfig, fmt.Errorf("Error: Non-null Username Required")
  100. }
  101. if flPassword == "" {
  102. oldState, err := term.SaveState(cli.inFd)
  103. if err != nil {
  104. return authconfig, err
  105. }
  106. fmt.Fprintf(cli.out, "Password: ")
  107. term.DisableEcho(cli.inFd, oldState)
  108. flPassword = readInput(cli.in, cli.out)
  109. fmt.Fprint(cli.out, "\n")
  110. term.RestoreTerminal(cli.inFd, oldState)
  111. if flPassword == "" {
  112. return authconfig, fmt.Errorf("Error: Password Required")
  113. }
  114. }
  115. authconfig.Username = flUser
  116. authconfig.Password = flPassword
  117. authconfig.ServerAddress = serverAddress
  118. authconfig.IdentityToken = ""
  119. return authconfig, nil
  120. }
  121. func readInput(in io.Reader, out io.Writer) string {
  122. reader := bufio.NewReader(in)
  123. line, _, err := reader.ReadLine()
  124. if err != nil {
  125. fmt.Fprintln(out, err.Error())
  126. os.Exit(1)
  127. }
  128. return string(line)
  129. }
  130. // getCredentials loads the user credentials from a credentials store.
  131. // The store is determined by the config file settings.
  132. func getCredentials(c *configfile.ConfigFile, serverAddress string) (types.AuthConfig, error) {
  133. s := loadCredentialsStore(c)
  134. return s.Get(serverAddress)
  135. }
  136. func getAllCredentials(c *configfile.ConfigFile) (map[string]types.AuthConfig, error) {
  137. s := loadCredentialsStore(c)
  138. return s.GetAll()
  139. }
  140. // storeCredentials saves the user credentials in a credentials store.
  141. // The store is determined by the config file settings.
  142. func storeCredentials(c *configfile.ConfigFile, auth types.AuthConfig) error {
  143. s := loadCredentialsStore(c)
  144. return s.Store(auth)
  145. }
  146. // eraseCredentials removes the user credentials from a credentials store.
  147. // The store is determined by the config file settings.
  148. func eraseCredentials(c *configfile.ConfigFile, serverAddress string) error {
  149. s := loadCredentialsStore(c)
  150. return s.Erase(serverAddress)
  151. }
  152. // loadCredentialsStore initializes a new credentials store based
  153. // in the settings provided in the configuration file.
  154. func loadCredentialsStore(c *configfile.ConfigFile) credentials.Store {
  155. if c.CredentialsStore != "" {
  156. return credentials.NewNativeStore(c)
  157. }
  158. return credentials.NewFileStore(c)
  159. }