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