github.com/kobeld/docker@v1.12.0-rc1/api/client/registry.go (about)

     1  package client
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"runtime"
    11  	"strings"
    12  
    13  	"golang.org/x/net/context"
    14  
    15  	"github.com/docker/docker/pkg/term"
    16  	"github.com/docker/docker/registry"
    17  	"github.com/docker/engine-api/types"
    18  	registrytypes "github.com/docker/engine-api/types/registry"
    19  )
    20  
    21  // ElectAuthServer returns the default registry to use (by asking the daemon)
    22  func (cli *DockerCli) ElectAuthServer(ctx context.Context) string {
    23  	// The daemon `/info` endpoint informs us of the default registry being
    24  	// used. This is essential in cross-platforms environment, where for
    25  	// example a Linux client might be interacting with a Windows daemon, hence
    26  	// the default registry URL might be Windows specific.
    27  	serverAddress := registry.IndexServer
    28  	if info, err := cli.client.Info(ctx); err != nil {
    29  		fmt.Fprintf(cli.out, "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
    30  	} else {
    31  		serverAddress = info.IndexServerAddress
    32  	}
    33  	return serverAddress
    34  }
    35  
    36  // EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload
    37  func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
    38  	buf, err := json.Marshal(authConfig)
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  	return base64.URLEncoding.EncodeToString(buf), nil
    43  }
    44  
    45  // RegistryAuthenticationPrivilegedFunc return a RequestPrivilegeFunc from the specified registry index info
    46  // for the given command.
    47  func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc {
    48  	return func() (string, error) {
    49  		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
    50  		indexServer := registry.GetAuthConfigKey(index)
    51  		authConfig, err := cli.ConfigureAuth("", "", indexServer, false)
    52  		if err != nil {
    53  			return "", err
    54  		}
    55  		return EncodeAuthToBase64(authConfig)
    56  	}
    57  }
    58  
    59  func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
    60  	if configDefault == "" {
    61  		fmt.Fprintf(cli.out, "%s: ", prompt)
    62  	} else {
    63  		fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
    64  	}
    65  }
    66  
    67  // ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the
    68  // default index, it uses the default index name for the daemon's platform,
    69  // not the client's platform.
    70  func (cli *DockerCli) ResolveAuthConfig(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
    71  	configKey := index.Name
    72  	if index.Official {
    73  		configKey = cli.ElectAuthServer(ctx)
    74  	}
    75  
    76  	a, _ := GetCredentials(cli.configFile, configKey)
    77  	return a
    78  }
    79  
    80  // RetrieveAuthConfigs return all credentials.
    81  func (cli *DockerCli) RetrieveAuthConfigs() map[string]types.AuthConfig {
    82  	acs, _ := GetAllCredentials(cli.configFile)
    83  	return acs
    84  }
    85  
    86  // ConfigureAuth returns an AuthConfig from the specified user, password and server.
    87  func (cli *DockerCli) ConfigureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
    88  	// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
    89  	if runtime.GOOS == "windows" {
    90  		cli.in = os.Stdin
    91  	}
    92  
    93  	authconfig, err := GetCredentials(cli.configFile, serverAddress)
    94  	if err != nil {
    95  		return authconfig, err
    96  	}
    97  
    98  	// Some links documenting this:
    99  	// - https://code.google.com/archive/p/mintty/issues/56
   100  	// - https://github.com/docker/docker/issues/15272
   101  	// - https://mintty.github.io/ (compatibility)
   102  	// Linux will hit this if you attempt `cat | docker login`, and Windows
   103  	// will hit this if you attempt docker login from mintty where stdin
   104  	// is a pipe, not a character based console.
   105  	if flPassword == "" && !cli.isTerminalIn {
   106  		return authconfig, fmt.Errorf("Error: Cannot perform an interactive logon from a non TTY device")
   107  	}
   108  
   109  	authconfig.Username = strings.TrimSpace(authconfig.Username)
   110  
   111  	if flUser = strings.TrimSpace(flUser); flUser == "" {
   112  		if isDefaultRegistry {
   113  			// if this is a defauly registry (docker hub), then display the following message.
   114  			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.")
   115  		}
   116  		cli.promptWithDefault("Username", authconfig.Username)
   117  		flUser = readInput(cli.in, cli.out)
   118  		flUser = strings.TrimSpace(flUser)
   119  		if flUser == "" {
   120  			flUser = authconfig.Username
   121  		}
   122  	}
   123  	if flUser == "" {
   124  		return authconfig, fmt.Errorf("Error: Non-null Username Required")
   125  	}
   126  	if flPassword == "" {
   127  		oldState, err := term.SaveState(cli.inFd)
   128  		if err != nil {
   129  			return authconfig, err
   130  		}
   131  		fmt.Fprintf(cli.out, "Password: ")
   132  		term.DisableEcho(cli.inFd, oldState)
   133  
   134  		flPassword = readInput(cli.in, cli.out)
   135  		fmt.Fprint(cli.out, "\n")
   136  
   137  		term.RestoreTerminal(cli.inFd, oldState)
   138  		if flPassword == "" {
   139  			return authconfig, fmt.Errorf("Error: Password Required")
   140  		}
   141  	}
   142  
   143  	authconfig.Username = flUser
   144  	authconfig.Password = flPassword
   145  	authconfig.ServerAddress = serverAddress
   146  	authconfig.IdentityToken = ""
   147  
   148  	return authconfig, nil
   149  }
   150  
   151  func readInput(in io.Reader, out io.Writer) string {
   152  	reader := bufio.NewReader(in)
   153  	line, _, err := reader.ReadLine()
   154  	if err != nil {
   155  		fmt.Fprintln(out, err.Error())
   156  		os.Exit(1)
   157  	}
   158  	return string(line)
   159  }