login.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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. "github.com/docker/docker/cliconfig"
  11. "github.com/docker/docker/cliconfig/credentials"
  12. flag "github.com/docker/docker/pkg/mflag"
  13. "github.com/docker/docker/pkg/term"
  14. "github.com/docker/engine-api/client"
  15. "github.com/docker/engine-api/types"
  16. )
  17. // CmdLogin logs in or registers 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. flEmail := cmd.String([]string{"e", "-email"}, "", "Email")
  28. cmd.ParseFlags(args, true)
  29. // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
  30. if runtime.GOOS == "windows" {
  31. cli.in = os.Stdin
  32. }
  33. var serverAddress string
  34. if len(cmd.Args()) > 0 {
  35. serverAddress = cmd.Arg(0)
  36. } else {
  37. serverAddress = cli.electAuthServer()
  38. }
  39. authConfig, err := cli.configureAuth(*flUser, *flPassword, *flEmail, serverAddress)
  40. if err != nil {
  41. return err
  42. }
  43. response, err := cli.client.RegistryLogin(authConfig)
  44. if err != nil {
  45. if client.IsErrUnauthorized(err) {
  46. if err2 := eraseCredentials(cli.configFile, authConfig.ServerAddress); err2 != nil {
  47. fmt.Fprintf(cli.out, "WARNING: could not save credentials: %v\n", err2)
  48. }
  49. }
  50. return err
  51. }
  52. if err := storeCredentials(cli.configFile, authConfig); err != nil {
  53. return fmt.Errorf("Error saving credentials: %v", err)
  54. }
  55. if response.Status != "" {
  56. fmt.Fprintf(cli.out, "%s\n", response.Status)
  57. }
  58. return nil
  59. }
  60. func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
  61. if configDefault == "" {
  62. fmt.Fprintf(cli.out, "%s: ", prompt)
  63. } else {
  64. fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
  65. }
  66. }
  67. func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress string) (types.AuthConfig, error) {
  68. authconfig, err := getCredentials(cli.configFile, serverAddress)
  69. if err != nil {
  70. return authconfig, err
  71. }
  72. authconfig.Username = strings.TrimSpace(authconfig.Username)
  73. if flUser = strings.TrimSpace(flUser); flUser == "" {
  74. cli.promptWithDefault("Username", authconfig.Username)
  75. flUser = readInput(cli.in, cli.out)
  76. flUser = strings.TrimSpace(flUser)
  77. if flUser == "" {
  78. flUser = authconfig.Username
  79. }
  80. }
  81. if flUser == "" {
  82. return authconfig, fmt.Errorf("Error: Non-null Username Required")
  83. }
  84. if flPassword == "" {
  85. oldState, err := term.SaveState(cli.inFd)
  86. if err != nil {
  87. return authconfig, err
  88. }
  89. fmt.Fprintf(cli.out, "Password: ")
  90. term.DisableEcho(cli.inFd, oldState)
  91. flPassword = readInput(cli.in, cli.out)
  92. fmt.Fprint(cli.out, "\n")
  93. term.RestoreTerminal(cli.inFd, oldState)
  94. if flPassword == "" {
  95. return authconfig, fmt.Errorf("Error: Password Required")
  96. }
  97. }
  98. // Assume that a different username means they may not want to use
  99. // the email from the config file, so prompt it
  100. if flUser != authconfig.Username {
  101. if flEmail == "" {
  102. cli.promptWithDefault("Email", authconfig.Email)
  103. flEmail = readInput(cli.in, cli.out)
  104. if flEmail == "" {
  105. flEmail = authconfig.Email
  106. }
  107. }
  108. } else {
  109. // However, if they don't override the username use the
  110. // email from the cmd line if specified. IOW, allow
  111. // then to change/override them. And if not specified, just
  112. // use what's in the config file
  113. if flEmail == "" {
  114. flEmail = authconfig.Email
  115. }
  116. }
  117. authconfig.Username = flUser
  118. authconfig.Password = flPassword
  119. authconfig.Email = flEmail
  120. authconfig.ServerAddress = serverAddress
  121. return authconfig, nil
  122. }
  123. func readInput(in io.Reader, out io.Writer) string {
  124. reader := bufio.NewReader(in)
  125. line, _, err := reader.ReadLine()
  126. if err != nil {
  127. fmt.Fprintln(out, err.Error())
  128. os.Exit(1)
  129. }
  130. return string(line)
  131. }
  132. // getCredentials loads the user credentials from a credentials store.
  133. // The store is determined by the config file settings.
  134. func getCredentials(c *cliconfig.ConfigFile, serverAddress string) (types.AuthConfig, error) {
  135. s := loadCredentialsStore(c)
  136. return s.Get(serverAddress)
  137. }
  138. // storeCredentials saves the user credentials in a credentials store.
  139. // The store is determined by the config file settings.
  140. func storeCredentials(c *cliconfig.ConfigFile, auth types.AuthConfig) error {
  141. s := loadCredentialsStore(c)
  142. return s.Store(auth)
  143. }
  144. // eraseCredentials removes the user credentials from a credentials store.
  145. // The store is determined by the config file settings.
  146. func eraseCredentials(c *cliconfig.ConfigFile, serverAddress string) error {
  147. s := loadCredentialsStore(c)
  148. return s.Erase(serverAddress)
  149. }
  150. // loadCredentialsStore initializes a new credentials store based
  151. // in the settings provided in the configuration file.
  152. func loadCredentialsStore(c *cliconfig.ConfigFile) credentials.Store {
  153. if c.CredentialsStore != "" {
  154. return credentials.NewNativeStore(c)
  155. }
  156. return credentials.NewFileStore(c)
  157. }