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