package client import ( "bufio" "encoding/json" "fmt" "io" "os" "strings" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" "github.com/docker/docker/cliconfig" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/term" "github.com/docker/docker/registry" ) // CmdLogin logs in or registers 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]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.IndexServer+"\" is the default.", true) cmd.Require(flag.Max, 1) var username, password, email string cmd.StringVar(&username, []string{"u", "-username"}, "", "Username") cmd.StringVar(&password, []string{"p", "-password"}, "", "Password") cmd.StringVar(&email, []string{"e", "-email"}, "", "Email") cmd.ParseFlags(args, true) serverAddress := registry.IndexServer if len(cmd.Args()) > 0 { serverAddress = cmd.Arg(0) } promptDefault := func(prompt string, configDefault string) { if configDefault == "" { fmt.Fprintf(cli.out, "%s: ", prompt) } else { fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault) } } readInput := func(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) } authconfig, ok := cli.configFile.AuthConfigs[serverAddress] if !ok { authconfig = cliconfig.AuthConfig{} } if username == "" { promptDefault("Username", authconfig.Username) username = readInput(cli.in, cli.out) username = strings.TrimSpace(username) if username == "" { username = authconfig.Username } } // Assume that a different username means they may not want to use // the password or email from the config file, so prompt them if username != authconfig.Username { if password == "" { oldState, err := term.SaveState(cli.inFd) if err != nil { return err } fmt.Fprintf(cli.out, "Password: ") term.DisableEcho(cli.inFd, oldState) password = readInput(cli.in, cli.out) fmt.Fprint(cli.out, "\n") term.RestoreTerminal(cli.inFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } } if email == "" { promptDefault("Email", authconfig.Email) email = readInput(cli.in, cli.out) if email == "" { email = authconfig.Email } } } else { // However, if they don't override the username use the // password or 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 password == "" { password = authconfig.Password } if email == "" { email = authconfig.Email } } authconfig.Username = username authconfig.Password = password authconfig.Email = email authconfig.ServerAddress = serverAddress cli.configFile.AuthConfigs[serverAddress] = authconfig serverResp, err := cli.call("POST", "/auth", cli.configFile.AuthConfigs[serverAddress], nil) if serverResp.statusCode == 401 { delete(cli.configFile.AuthConfigs, serverAddress) if err2 := cli.configFile.Save(); err2 != nil { fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2) } return err } if err != nil { return err } defer serverResp.body.Close() var response types.AuthResponse if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil { // Upon error, remove entry delete(cli.configFile.AuthConfigs, serverAddress) return err } if err := cli.configFile.Save(); err != nil { return fmt.Errorf("Error saving config file: %v", err) } fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s\n", cli.configFile.Filename()) if response.Status != "" { fmt.Fprintf(cli.out, "%s\n", response.Status) } return nil }