github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/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  	DetachKeys           string                      `json:"detachKeys,omitempty"`
    32  	CredentialsStore     string                      `json:"credsStore,omitempty"`
    33  	Filename             string                      `json:"-"` // Note: for internal use only
    34  	ServiceInspectFormat string                      `json:"serviceInspectFormat,omitempty"`
    35  }
    36  
    37  // LegacyLoadFromReader reads the non-nested configuration data given and sets up the
    38  // auth config information with given directory and populates the receiver object
    39  func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
    40  	b, err := ioutil.ReadAll(configData)
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
    46  		arr := strings.Split(string(b), "\n")
    47  		if len(arr) < 2 {
    48  			return fmt.Errorf("The Auth config file is empty")
    49  		}
    50  		authConfig := types.AuthConfig{}
    51  		origAuth := strings.Split(arr[0], " = ")
    52  		if len(origAuth) != 2 {
    53  			return fmt.Errorf("Invalid Auth config file")
    54  		}
    55  		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
    56  		if err != nil {
    57  			return err
    58  		}
    59  		authConfig.ServerAddress = defaultIndexserver
    60  		configFile.AuthConfigs[defaultIndexserver] = authConfig
    61  	} else {
    62  		for k, authConfig := range configFile.AuthConfigs {
    63  			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
    64  			if err != nil {
    65  				return err
    66  			}
    67  			authConfig.Auth = ""
    68  			authConfig.ServerAddress = k
    69  			configFile.AuthConfigs[k] = authConfig
    70  		}
    71  	}
    72  	return nil
    73  }
    74  
    75  // LoadFromReader reads the configuration data given and sets up the auth config
    76  // information with given directory and populates the receiver object
    77  func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
    78  	if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
    79  		return err
    80  	}
    81  	var err error
    82  	for addr, ac := range configFile.AuthConfigs {
    83  		ac.Username, ac.Password, err = decodeAuth(ac.Auth)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		ac.Auth = ""
    88  		ac.ServerAddress = addr
    89  		configFile.AuthConfigs[addr] = ac
    90  	}
    91  	return nil
    92  }
    93  
    94  // ContainsAuth returns whether there is authentication configured
    95  // in this file or not.
    96  func (configFile *ConfigFile) ContainsAuth() bool {
    97  	return configFile.CredentialsStore != "" ||
    98  		(configFile.AuthConfigs != nil && len(configFile.AuthConfigs) > 0)
    99  }
   100  
   101  // SaveToWriter encodes and writes out all the authorization information to
   102  // the given writer
   103  func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
   104  	// Encode sensitive data into a new/temp struct
   105  	tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
   106  	for k, authConfig := range configFile.AuthConfigs {
   107  		authCopy := authConfig
   108  		// encode and save the authstring, while blanking out the original fields
   109  		authCopy.Auth = encodeAuth(&authCopy)
   110  		authCopy.Username = ""
   111  		authCopy.Password = ""
   112  		authCopy.ServerAddress = ""
   113  		tmpAuthConfigs[k] = authCopy
   114  	}
   115  
   116  	saveAuthConfigs := configFile.AuthConfigs
   117  	configFile.AuthConfigs = tmpAuthConfigs
   118  	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
   119  
   120  	data, err := json.MarshalIndent(configFile, "", "\t")
   121  	if err != nil {
   122  		return err
   123  	}
   124  	_, err = writer.Write(data)
   125  	return err
   126  }
   127  
   128  // Save encodes and writes out all the authorization information
   129  func (configFile *ConfigFile) Save() error {
   130  	if configFile.Filename == "" {
   131  		return fmt.Errorf("Can't save config with empty filename")
   132  	}
   133  
   134  	if err := os.MkdirAll(filepath.Dir(configFile.Filename), 0700); err != nil {
   135  		return err
   136  	}
   137  	f, err := os.OpenFile(configFile.Filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	defer f.Close()
   142  	return configFile.SaveToWriter(f)
   143  }
   144  
   145  // encodeAuth creates a base64 encoded string to containing authorization information
   146  func encodeAuth(authConfig *types.AuthConfig) string {
   147  	if authConfig.Username == "" && authConfig.Password == "" {
   148  		return ""
   149  	}
   150  
   151  	authStr := authConfig.Username + ":" + authConfig.Password
   152  	msg := []byte(authStr)
   153  	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
   154  	base64.StdEncoding.Encode(encoded, msg)
   155  	return string(encoded)
   156  }
   157  
   158  // decodeAuth decodes a base64 encoded string and returns username and password
   159  func decodeAuth(authStr string) (string, string, error) {
   160  	if authStr == "" {
   161  		return "", "", nil
   162  	}
   163  
   164  	decLen := base64.StdEncoding.DecodedLen(len(authStr))
   165  	decoded := make([]byte, decLen)
   166  	authByte := []byte(authStr)
   167  	n, err := base64.StdEncoding.Decode(decoded, authByte)
   168  	if err != nil {
   169  		return "", "", err
   170  	}
   171  	if n > decLen {
   172  		return "", "", fmt.Errorf("Something went wrong decoding auth config")
   173  	}
   174  	arr := strings.SplitN(string(decoded), ":", 2)
   175  	if len(arr) != 2 {
   176  		return "", "", fmt.Errorf("Invalid auth configuration file")
   177  	}
   178  	password := strings.Trim(arr[1], "\x00")
   179  	return arr[0], password, nil
   180  }