github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/registry.go (about) 1 package command 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/api/types" 16 registrytypes "github.com/docker/docker/api/types/registry" 17 "github.com/docker/docker/pkg/term" 18 "github.com/docker/docker/reference" 19 "github.com/docker/docker/registry" 20 ) 21 22 // ElectAuthServer returns the default registry to use (by asking the daemon) 23 func ElectAuthServer(ctx context.Context, cli *DockerCli) string { 24 // The daemon `/info` endpoint informs us of the default registry being 25 // used. This is essential in cross-platforms environment, where for 26 // example a Linux client might be interacting with a Windows daemon, hence 27 // the default registry URL might be Windows specific. 28 serverAddress := registry.IndexServer 29 if info, err := cli.Client().Info(ctx); err != nil { 30 fmt.Fprintf(cli.Out(), "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress) 31 } else { 32 serverAddress = info.IndexServerAddress 33 } 34 return serverAddress 35 } 36 37 // EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload 38 func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) { 39 buf, err := json.Marshal(authConfig) 40 if err != nil { 41 return "", err 42 } 43 return base64.URLEncoding.EncodeToString(buf), nil 44 } 45 46 // RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info 47 // for the given command. 48 func RegistryAuthenticationPrivilegedFunc(cli *DockerCli, index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc { 49 return func() (string, error) { 50 fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName) 51 indexServer := registry.GetAuthConfigKey(index) 52 isDefaultRegistry := indexServer == ElectAuthServer(context.Background(), cli) 53 authConfig, err := ConfigureAuth(cli, "", "", indexServer, isDefaultRegistry) 54 if err != nil { 55 return "", err 56 } 57 return EncodeAuthToBase64(authConfig) 58 } 59 } 60 61 // ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the 62 // default index, it uses the default index name for the daemon's platform, 63 // not the client's platform. 64 func ResolveAuthConfig(ctx context.Context, cli *DockerCli, index *registrytypes.IndexInfo) types.AuthConfig { 65 configKey := index.Name 66 if index.Official { 67 configKey = ElectAuthServer(ctx, cli) 68 } 69 70 a, _ := cli.CredentialsStore().Get(configKey) 71 return a 72 } 73 74 // ConfigureAuth returns an AuthConfig from the specified user, password and server. 75 func ConfigureAuth(cli *DockerCli, flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) { 76 // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210 77 if runtime.GOOS == "windows" { 78 cli.in = NewInStream(os.Stdin) 79 } 80 81 if !isDefaultRegistry { 82 serverAddress = registry.ConvertToHostname(serverAddress) 83 } 84 85 authconfig, err := cli.CredentialsStore().Get(serverAddress) 86 if err != nil { 87 return authconfig, err 88 } 89 90 // Some links documenting this: 91 // - https://code.google.com/archive/p/mintty/issues/56 92 // - https://github.com/docker/docker/issues/15272 93 // - https://mintty.github.io/ (compatibility) 94 // Linux will hit this if you attempt `cat | docker login`, and Windows 95 // will hit this if you attempt docker login from mintty where stdin 96 // is a pipe, not a character based console. 97 if flPassword == "" && !cli.In().IsTerminal() { 98 return authconfig, fmt.Errorf("Error: Cannot perform an interactive login from a non TTY device") 99 } 100 101 authconfig.Username = strings.TrimSpace(authconfig.Username) 102 103 if flUser = strings.TrimSpace(flUser); flUser == "" { 104 if isDefaultRegistry { 105 // if this is a default registry (docker hub), then display the following message. 106 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.") 107 } 108 promptWithDefault(cli.Out(), "Username", authconfig.Username) 109 flUser = readInput(cli.In(), cli.Out()) 110 flUser = strings.TrimSpace(flUser) 111 if flUser == "" { 112 flUser = authconfig.Username 113 } 114 } 115 if flUser == "" { 116 return authconfig, fmt.Errorf("Error: Non-null Username Required") 117 } 118 if flPassword == "" { 119 oldState, err := term.SaveState(cli.In().FD()) 120 if err != nil { 121 return authconfig, err 122 } 123 fmt.Fprintf(cli.Out(), "Password: ") 124 term.DisableEcho(cli.In().FD(), oldState) 125 126 flPassword = readInput(cli.In(), cli.Out()) 127 fmt.Fprint(cli.Out(), "\n") 128 129 term.RestoreTerminal(cli.In().FD(), oldState) 130 if flPassword == "" { 131 return authconfig, fmt.Errorf("Error: Password Required") 132 } 133 } 134 135 authconfig.Username = flUser 136 authconfig.Password = flPassword 137 authconfig.ServerAddress = serverAddress 138 authconfig.IdentityToken = "" 139 140 return authconfig, nil 141 } 142 143 func readInput(in io.Reader, out io.Writer) string { 144 reader := bufio.NewReader(in) 145 line, _, err := reader.ReadLine() 146 if err != nil { 147 fmt.Fprintln(out, err.Error()) 148 os.Exit(1) 149 } 150 return string(line) 151 } 152 153 func promptWithDefault(out io.Writer, prompt string, configDefault string) { 154 if configDefault == "" { 155 fmt.Fprintf(out, "%s: ", prompt) 156 } else { 157 fmt.Fprintf(out, "%s (%s): ", prompt, configDefault) 158 } 159 } 160 161 // RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image 162 func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image string) (string, error) { 163 // Retrieve encoded auth token from the image reference 164 authConfig, err := resolveAuthConfigFromImage(ctx, cli, image) 165 if err != nil { 166 return "", err 167 } 168 encodedAuth, err := EncodeAuthToBase64(authConfig) 169 if err != nil { 170 return "", err 171 } 172 return encodedAuth, nil 173 } 174 175 // resolveAuthConfigFromImage retrieves that AuthConfig using the image string 176 func resolveAuthConfigFromImage(ctx context.Context, cli *DockerCli, image string) (types.AuthConfig, error) { 177 registryRef, err := reference.ParseNamed(image) 178 if err != nil { 179 return types.AuthConfig{}, err 180 } 181 repoInfo, err := registry.ParseRepositoryInfo(registryRef) 182 if err != nil { 183 return types.AuthConfig{}, err 184 } 185 return ResolveAuthConfig(ctx, cli, repoInfo.Index), nil 186 }