| package client |
| |
| import ( |
| "bufio" |
| "fmt" |
| "io" |
| "os" |
| "runtime" |
| "strings" |
| |
| "golang.org/x/net/context" |
| |
| Cli "github.com/docker/docker/cli" |
| "github.com/docker/docker/cliconfig" |
| "github.com/docker/docker/cliconfig/credentials" |
| flag "github.com/docker/docker/pkg/mflag" |
| "github.com/docker/docker/pkg/term" |
| "github.com/docker/engine-api/types" |
| ) |
| |
| // 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. |
| // |
| // Usage: docker login SERVER |
| func (cli *DockerCli) CmdLogin(args ...string) error { |
| cmd := Cli.Subcmd("login", []string{"[SERVER]"}, Cli.DockerCommands["login"].Description+".\nIf no server is specified, the default is defined by the daemon.", true) |
| cmd.Require(flag.Max, 1) |
| |
| flUser := cmd.String([]string{"u", "-username"}, "", "Username") |
| flPassword := cmd.String([]string{"p", "-password"}, "", "Password") |
| |
| // Deprecated in 1.11: Should be removed in docker 1.13 |
| cmd.String([]string{"#e", "#-email"}, "", "Email") |
| |
| cmd.ParseFlags(args, true) |
| |
| // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210 |
| if runtime.GOOS == "windows" { |
| cli.in = os.Stdin |
| } |
| |
| 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, serverAddress, isDefaultRegistry) |
| if err != nil { |
| return err |
| } |
| |
| response, err := cli.client.RegistryLogin(context.Background(), authConfig) |
| if err != nil { |
| return err |
| } |
| |
| if response.IdentityToken != "" { |
| authConfig.Password = "" |
| authConfig.IdentityToken = response.IdentityToken |
| } |
| if err := storeCredentials(cli.configFile, authConfig); err != nil { |
| return fmt.Errorf("Error saving credentials: %v", err) |
| } |
| |
| if response.Status != "" { |
| fmt.Fprintln(cli.out, response.Status) |
| } |
| return nil |
| } |
| |
| func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) { |
| if configDefault == "" { |
| fmt.Fprintf(cli.out, "%s: ", prompt) |
| } else { |
| fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) |
| } |
| } |
| |
| 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 |
| } |
| |
| 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) |
| if flUser == "" { |
| flUser = authconfig.Username |
| } |
| } |
| |
| if flUser == "" { |
| return authconfig, fmt.Errorf("Error: Non-null Username Required") |
| } |
| |
| if flPassword == "" { |
| oldState, err := term.SaveState(cli.inFd) |
| if err != nil { |
| return authconfig, err |
| } |
| fmt.Fprintf(cli.out, "Password: ") |
| term.DisableEcho(cli.inFd, oldState) |
| |
| flPassword = readInput(cli.in, cli.out) |
| fmt.Fprint(cli.out, "\n") |
| |
| term.RestoreTerminal(cli.inFd, oldState) |
| if flPassword == "" { |
| return authconfig, fmt.Errorf("Error: Password Required") |
| } |
| } |
| |
| authconfig.Username = flUser |
| authconfig.Password = flPassword |
| authconfig.ServerAddress = serverAddress |
| authconfig.IdentityToken = "" |
| |
| return authconfig, nil |
| } |
| |
| func readInput(in io.Reader, out io.Writer) string { |
| reader := bufio.NewReader(in) |
| line, _, err := reader.ReadLine() |
| if err != nil { |
| fmt.Fprintln(out, err.Error()) |
| os.Exit(1) |
| } |
| return string(line) |
| } |
| |
| // getCredentials loads the user credentials from a credentials store. |
| // The store is determined by the config file settings. |
| func getCredentials(c *cliconfig.ConfigFile, serverAddress string) (types.AuthConfig, error) { |
| s := loadCredentialsStore(c) |
| return s.Get(serverAddress) |
| } |
| |
| func getAllCredentials(c *cliconfig.ConfigFile) (map[string]types.AuthConfig, error) { |
| s := loadCredentialsStore(c) |
| return s.GetAll() |
| } |
| |
| // storeCredentials saves the user credentials in a credentials store. |
| // The store is determined by the config file settings. |
| func storeCredentials(c *cliconfig.ConfigFile, auth types.AuthConfig) error { |
| s := loadCredentialsStore(c) |
| return s.Store(auth) |
| } |
| |
| // eraseCredentials removes the user credentials from a credentials store. |
| // The store is determined by the config file settings. |
| func eraseCredentials(c *cliconfig.ConfigFile, serverAddress string) error { |
| s := loadCredentialsStore(c) |
| return s.Erase(serverAddress) |
| } |
| |
| // loadCredentialsStore initializes a new credentials store based |
| // in the settings provided in the configuration file. |
| func loadCredentialsStore(c *cliconfig.ConfigFile) credentials.Store { |
| if c.CredentialsStore != "" { |
| return credentials.NewNativeStore(c) |
| } |
| return credentials.NewFileStore(c) |
| } |