2016-06-10 06:04:29 -04:00
package client
import (
"bufio"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
"runtime"
"strings"
"golang.org/x/net/context"
2016-09-06 14:18:12 -04:00
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
2016-06-10 06:04:29 -04:00
"github.com/docker/docker/pkg/term"
2016-06-15 14:50:49 -04:00
"github.com/docker/docker/reference"
2016-06-10 06:04:29 -04:00
"github.com/docker/docker/registry"
)
// ElectAuthServer returns the default registry to use (by asking the daemon)
func ( cli * DockerCli ) ElectAuthServer ( ctx context . Context ) string {
// The daemon `/info` endpoint informs us of the default registry being
// used. This is essential in cross-platforms environment, where for
// example a Linux client might be interacting with a Windows daemon, hence
// the default registry URL might be Windows specific.
serverAddress := registry . IndexServer
if info , err := cli . client . Info ( ctx ) ; err != nil {
fmt . Fprintf ( cli . out , "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n" , err , serverAddress )
} else {
serverAddress = info . IndexServerAddress
}
return serverAddress
}
// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload
func EncodeAuthToBase64 ( authConfig types . AuthConfig ) ( string , error ) {
buf , err := json . Marshal ( authConfig )
if err != nil {
return "" , err
}
return base64 . URLEncoding . EncodeToString ( buf ) , nil
}
2016-06-29 05:26:42 -04:00
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
2016-06-10 06:04:29 -04:00
// for the given command.
func ( cli * DockerCli ) RegistryAuthenticationPrivilegedFunc ( index * registrytypes . IndexInfo , cmdName string ) types . RequestPrivilegeFunc {
return func ( ) ( string , error ) {
fmt . Fprintf ( cli . out , "\nPlease login prior to %s:\n" , cmdName )
indexServer := registry . GetAuthConfigKey ( index )
2016-05-30 06:47:49 -04:00
isDefaultRegistry := indexServer == cli . ElectAuthServer ( context . Background ( ) )
authConfig , err := cli . ConfigureAuth ( "" , "" , indexServer , isDefaultRegistry )
2016-06-10 06:04:29 -04:00
if err != nil {
return "" , err
}
return EncodeAuthToBase64 ( authConfig )
}
}
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 )
}
}
// ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the
// default index, it uses the default index name for the daemon's platform,
// not the client's platform.
func ( cli * DockerCli ) ResolveAuthConfig ( ctx context . Context , index * registrytypes . IndexInfo ) types . AuthConfig {
configKey := index . Name
if index . Official {
configKey = cli . ElectAuthServer ( ctx )
}
a , _ := GetCredentials ( cli . configFile , configKey )
return a
}
// RetrieveAuthConfigs return all credentials.
func ( cli * DockerCli ) RetrieveAuthConfigs ( ) map [ string ] types . AuthConfig {
acs , _ := GetAllCredentials ( cli . configFile )
return acs
}
// ConfigureAuth returns an AuthConfig from the specified user, password and server.
func ( cli * DockerCli ) ConfigureAuth ( flUser , flPassword , serverAddress string , isDefaultRegistry bool ) ( types . AuthConfig , error ) {
// On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210
if runtime . GOOS == "windows" {
2016-08-29 11:52:05 -04:00
cli . in = NewInStream ( os . Stdin )
2016-06-10 06:04:29 -04:00
}
2016-05-30 06:47:49 -04:00
if ! isDefaultRegistry {
serverAddress = registry . ConvertToHostname ( serverAddress )
}
2016-06-10 06:04:29 -04:00
authconfig , err := GetCredentials ( cli . configFile , serverAddress )
if err != nil {
return authconfig , err
}
// Some links documenting this:
// - https://code.google.com/archive/p/mintty/issues/56
// - https://github.com/docker/docker/issues/15272
// - https://mintty.github.io/ (compatibility)
// Linux will hit this if you attempt `cat | docker login`, and Windows
// will hit this if you attempt docker login from mintty where stdin
// is a pipe, not a character based console.
2016-08-29 11:52:05 -04:00
if flPassword == "" && ! cli . In ( ) . IsTerminal ( ) {
2016-06-29 05:26:42 -04:00
return authconfig , fmt . Errorf ( "Error: Cannot perform an interactive login from a non TTY device" )
2016-06-10 06:04:29 -04:00
}
authconfig . Username = strings . TrimSpace ( authconfig . Username )
if flUser = strings . TrimSpace ( flUser ) ; flUser == "" {
if isDefaultRegistry {
2016-06-21 03:28:30 -04:00
// if this is a default registry (docker hub), then display the following message.
2016-06-10 06:04:29 -04:00
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 == "" {
2016-08-29 11:52:05 -04:00
oldState , err := term . SaveState ( cli . In ( ) . FD ( ) )
2016-06-10 06:04:29 -04:00
if err != nil {
return authconfig , err
}
fmt . Fprintf ( cli . out , "Password: " )
2016-08-29 11:52:05 -04:00
term . DisableEcho ( cli . In ( ) . FD ( ) , oldState )
2016-06-10 06:04:29 -04:00
flPassword = readInput ( cli . in , cli . out )
fmt . Fprint ( cli . out , "\n" )
2016-08-29 11:52:05 -04:00
term . RestoreTerminal ( cli . In ( ) . FD ( ) , oldState )
2016-06-10 06:04:29 -04:00
if flPassword == "" {
return authconfig , fmt . Errorf ( "Error: Password Required" )
}
}
authconfig . Username = flUser
authconfig . Password = flPassword
authconfig . ServerAddress = serverAddress
authconfig . IdentityToken = ""
return authconfig , nil
}
2016-06-29 20:08:00 -04:00
// resolveAuthConfigFromImage retrieves that AuthConfig using the image string
func ( cli * DockerCli ) resolveAuthConfigFromImage ( ctx context . Context , image string ) ( types . AuthConfig , error ) {
2016-06-15 14:50:49 -04:00
registryRef , err := reference . ParseNamed ( image )
if err != nil {
return types . AuthConfig { } , err
}
repoInfo , err := registry . ParseRepositoryInfo ( registryRef )
if err != nil {
return types . AuthConfig { } , err
}
authConfig := cli . ResolveAuthConfig ( ctx , repoInfo . Index )
return authConfig , nil
}
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image
func ( cli * DockerCli ) RetrieveAuthTokenFromImage ( ctx context . Context , image string ) ( string , error ) {
// Retrieve encoded auth token from the image reference
2016-06-29 20:08:00 -04:00
authConfig , err := cli . resolveAuthConfigFromImage ( ctx , image )
2016-06-15 14:50:49 -04:00
if err != nil {
return "" , err
}
encodedAuth , err := EncodeAuthToBase64 ( authConfig )
if err != nil {
return "" , err
}
return encodedAuth , nil
}
2016-06-10 06:04:29 -04:00
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 )
}