gopkg.in/dotcloud/docker.v1@v1.13.1/cliconfig/configfile/file.go (about)

     1  package configfile
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/docker/docker/api/types"
    14  )
    15  
    16  const (
    17  	// This constant is only used for really old config files when the
    18  	// URL wasn't saved as part of the config file and it was just
    19  	// assumed to be this value.
    20  	defaultIndexserver = "https://index.docker.io/v1/"
    21  )
    22  
    23  // ConfigFile ~/.docker/config.json file info
    24  type ConfigFile struct {
    25  	AuthConfigs          map[string]types.AuthConfig `json:"auths"`
    26  	HTTPHeaders          map[string]string           `json:"HttpHeaders,omitempty"`
    27  	PsFormat             string                      `json:"psFormat,omitempty"`
    28  	ImagesFormat         string                      `json:"imagesFormat,omitempty"`
    29  	NetworksFormat       string                      `json:"networksFormat,omitempty"`
    30  	VolumesFormat        string                      `json:"volumesFormat,omitempty"`
    31  	StatsFormat          string                      `json:"statsFormat,omitempty"`
    32  	DetachKeys           string                      `json:"detachKeys,omitempty"`
    33  	CredentialsStore     string                      `json:"credsStore,omitempty"`
    34  	CredentialHelpers    map[string]string           `json:"credHelpers,omitempty"`
    35  	Filename             string                      `json:"-"` // Note: for internal use only
    36  	ServiceInspectFormat string                      `json:"serviceInspectFormat,omitempty"`
    37  }
    38  
    39  // LegacyLoadFromReader reads the non-nested configuration data given and sets up the
    40  // auth config information with given directory and populates the receiver object
    41  func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
    42  	b, err := ioutil.ReadAll(configData)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
    48  		arr := strings.Split(string(b), "\n")
    49  		if len(arr) < 2 {
    50  			return fmt.Errorf("The Auth config file is empty")
    51  		}
    52  		authConfig := types.AuthConfig{}
    53  		origAuth := strings.Split(arr[0], " = ")
    54  		if len(origAuth) != 2 {
    55  			return fmt.Errorf("Invalid Auth config file")
    56  		}
    57  		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
    58  		if err != nil {
    59  			return err
    60  		}
    61  		authConfig.ServerAddress = defaultIndexserver
    62  		configFile.AuthConfigs[defaultIndexserver] = authConfig
    63  	} else {
    64  		for k, authConfig := range configFile.AuthConfigs {
    65  			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
    66  			if err != nil {
    67  				return err
    68  			}
    69  			authConfig.Auth = ""
    70  			authConfig.ServerAddress = k
    71  			configFile.AuthConfigs[k] = authConfig
    72  		}
    73  	}
    74  	return nil
    75  }
    76  
    77  // LoadFromReader reads the configuration data given and sets up the auth config
    78  // information with given directory and populates the receiver object
    79  func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
    80  	if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
    81  		return err
    82  	}
    83  	var err error
    84  	for addr, ac := range configFile.AuthConfigs {
    85  		ac.Username, ac.Password, err = decodeAuth(ac.Auth)
    86  		if err != nil {
    87  			return err
    88  		}
    89  		ac.Auth = ""
    90  		ac.ServerAddress = addr
    91  		configFile.AuthConfigs[addr] = ac
    92  	}
    93  	return nil
    94  }
    95  
    96  // ContainsAuth returns whether there is authentication configured
    97  // in this file or not.
    98  func (configFile *ConfigFile) ContainsAuth() bool {
    99  	return configFile.CredentialsStore != "" ||
   100  		len(configFile.CredentialHelpers) > 0 ||
   101  		len(configFile.AuthConfigs) > 0
   102  }
   103  
   104  // SaveToWriter encodes and writes out all the authorization information to
   105  // the given writer
   106  func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
   107  	// Encode sensitive data into a new/temp struct
   108  	tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
   109  	for k, authConfig := range configFile.AuthConfigs {
   110  		authCopy := authConfig
   111  		// encode and save the authstring, while blanking out the original fields
   112  		authCopy.Auth = encodeAuth(&authCopy)
   113  		authCopy.Username = ""
   114  		authCopy.Password = ""
   115  		authCopy.ServerAddress = ""
   116  		tmpAuthConfigs[k] = authCopy
   117  	}
   118  
   119  	saveAuthConfigs := configFile.AuthConfigs
   120  	configFile.AuthConfigs = tmpAuthConfigs
   121  	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
   122  
   123  	data, err := json.MarshalIndent(configFile, "", "\t")
   124  	if err != nil {
   125  		return err
   126  	}
   127  	_, err = writer.Write(data)
   128  	return err
   129  }
   130  
   131  // Save encodes and writes out all the authorization information
   132  func (configFile *ConfigFile) Save() error {
   133  	if configFile.Filename == "" {
   134  		return fmt.Errorf("Can't save config with empty filename")
   135  	}
   136  
   137  	if err := os.MkdirAll(filepath.Dir(configFile.Filename), 0700); err != nil {
   138  		return err
   139  	}
   140  	f, err := os.OpenFile(configFile.Filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	defer f.Close()
   145  	return configFile.SaveToWriter(f)
   146  }
   147  
   148  // encodeAuth creates a base64 encoded string to containing authorization information
   149  func encodeAuth(authConfig *types.AuthConfig) string {
   150  	if authConfig.Username == "" && authConfig.Password == "" {
   151  		return ""
   152  	}
   153  
   154  	authStr := authConfig.Username + ":" + authConfig.Password
   155  	msg := []byte(authStr)
   156  	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
   157  	base64.StdEncoding.Encode(encoded, msg)
   158  	return string(encoded)
   159  }
   160  
   161  // decodeAuth decodes a base64 encoded string and returns username and password
   162  func decodeAuth(authStr string) (string, string, error) {
   163  	if authStr == "" {
   164  		return "", "", nil
   165  	}
   166  
   167  	decLen := base64.StdEncoding.DecodedLen(len(authStr))
   168  	decoded := make([]byte, decLen)
   169  	authByte := []byte(authStr)
   170  	n, err := base64.StdEncoding.Decode(decoded, authByte)
   171  	if err != nil {
   172  		return "", "", err
   173  	}
   174  	if n > decLen {
   175  		return "", "", fmt.Errorf("Something went wrong decoding auth config")
   176  	}
   177  	arr := strings.SplitN(string(decoded), ":", 2)
   178  	if len(arr) != 2 {
   179  		return "", "", fmt.Errorf("Invalid auth configuration file")
   180  	}
   181  	password := strings.Trim(arr[1], "\x00")
   182  	return arr[0], password, nil
   183  }