github.com/carlanton/docker@v1.8.0-rc1/cliconfig/config.go (about)

     1  package cliconfig
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/docker/docker/pkg/homedir"
    13  	"github.com/docker/docker/pkg/system"
    14  )
    15  
    16  const (
    17  	// ConfigFile is the name of config file
    18  	ConfigFileName = "config.json"
    19  	oldConfigfile  = ".dockercfg"
    20  
    21  	// This constant is only used for really old config files when the
    22  	// URL wasn't saved as part of the config file and it was just
    23  	// assumed to be this value.
    24  	defaultIndexserver = "https://index.docker.io/v1/"
    25  )
    26  
    27  var (
    28  	configDir = os.Getenv("DOCKER_CONFIG")
    29  )
    30  
    31  func init() {
    32  	if configDir == "" {
    33  		configDir = filepath.Join(homedir.Get(), ".docker")
    34  	}
    35  }
    36  
    37  // ConfigDir returns the directory the configuration file is stored in
    38  func ConfigDir() string {
    39  	return configDir
    40  }
    41  
    42  // SetConfigDir sets the directory the configuration file is stored in
    43  func SetConfigDir(dir string) {
    44  	configDir = dir
    45  }
    46  
    47  // AuthConfig contains authorization information for connecting to a Registry
    48  type AuthConfig struct {
    49  	Username      string `json:"username,omitempty"`
    50  	Password      string `json:"password,omitempty"`
    51  	Auth          string `json:"auth"`
    52  	Email         string `json:"email"`
    53  	ServerAddress string `json:"serveraddress,omitempty"`
    54  }
    55  
    56  // ConfigFile ~/.docker/config.json file info
    57  type ConfigFile struct {
    58  	AuthConfigs map[string]AuthConfig `json:"auths"`
    59  	HTTPHeaders map[string]string     `json:"HttpHeaders,omitempty"`
    60  	PsFormat    string                `json:"psFormat,omitempty"`
    61  	filename    string                // Note: not serialized - for internal use only
    62  }
    63  
    64  // NewConfigFile initilizes an empty configuration file for the given filename 'fn'
    65  func NewConfigFile(fn string) *ConfigFile {
    66  	return &ConfigFile{
    67  		AuthConfigs: make(map[string]AuthConfig),
    68  		HTTPHeaders: make(map[string]string),
    69  		filename:    fn,
    70  	}
    71  }
    72  
    73  // Load reads the configuration files in the given directory, and sets up
    74  // the auth config information and return values.
    75  // FIXME: use the internal golang config parser
    76  func Load(configDir string) (*ConfigFile, error) {
    77  	if configDir == "" {
    78  		configDir = ConfigDir()
    79  	}
    80  
    81  	configFile := ConfigFile{
    82  		AuthConfigs: make(map[string]AuthConfig),
    83  		filename:    filepath.Join(configDir, ConfigFileName),
    84  	}
    85  
    86  	// Try happy path first - latest config file
    87  	if _, err := os.Stat(configFile.filename); err == nil {
    88  		file, err := os.Open(configFile.filename)
    89  		if err != nil {
    90  			return &configFile, err
    91  		}
    92  		defer file.Close()
    93  
    94  		if err := json.NewDecoder(file).Decode(&configFile); err != nil {
    95  			return &configFile, err
    96  		}
    97  
    98  		for addr, ac := range configFile.AuthConfigs {
    99  			ac.Username, ac.Password, err = DecodeAuth(ac.Auth)
   100  			if err != nil {
   101  				return &configFile, err
   102  			}
   103  			ac.Auth = ""
   104  			ac.ServerAddress = addr
   105  			configFile.AuthConfigs[addr] = ac
   106  		}
   107  
   108  		return &configFile, nil
   109  	} else if !os.IsNotExist(err) {
   110  		// if file is there but we can't stat it for any reason other
   111  		// than it doesn't exist then stop
   112  		return &configFile, err
   113  	}
   114  
   115  	// Can't find latest config file so check for the old one
   116  	confFile := filepath.Join(homedir.Get(), oldConfigfile)
   117  	if _, err := os.Stat(confFile); err != nil {
   118  		return &configFile, nil //missing file is not an error
   119  	}
   120  
   121  	b, err := ioutil.ReadFile(confFile)
   122  	if err != nil {
   123  		return &configFile, err
   124  	}
   125  
   126  	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
   127  		arr := strings.Split(string(b), "\n")
   128  		if len(arr) < 2 {
   129  			return &configFile, fmt.Errorf("The Auth config file is empty")
   130  		}
   131  		authConfig := AuthConfig{}
   132  		origAuth := strings.Split(arr[0], " = ")
   133  		if len(origAuth) != 2 {
   134  			return &configFile, fmt.Errorf("Invalid Auth config file")
   135  		}
   136  		authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1])
   137  		if err != nil {
   138  			return &configFile, err
   139  		}
   140  		origEmail := strings.Split(arr[1], " = ")
   141  		if len(origEmail) != 2 {
   142  			return &configFile, fmt.Errorf("Invalid Auth config file")
   143  		}
   144  		authConfig.Email = origEmail[1]
   145  		authConfig.ServerAddress = defaultIndexserver
   146  		configFile.AuthConfigs[defaultIndexserver] = authConfig
   147  	} else {
   148  		for k, authConfig := range configFile.AuthConfigs {
   149  			authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth)
   150  			if err != nil {
   151  				return &configFile, err
   152  			}
   153  			authConfig.Auth = ""
   154  			authConfig.ServerAddress = k
   155  			configFile.AuthConfigs[k] = authConfig
   156  		}
   157  	}
   158  	return &configFile, nil
   159  }
   160  
   161  // Save encodes and writes out all the authorization information
   162  func (configFile *ConfigFile) Save() error {
   163  	// Encode sensitive data into a new/temp struct
   164  	tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
   165  	for k, authConfig := range configFile.AuthConfigs {
   166  		authCopy := authConfig
   167  		// encode and save the authstring, while blanking out the original fields
   168  		authCopy.Auth = EncodeAuth(&authCopy)
   169  		authCopy.Username = ""
   170  		authCopy.Password = ""
   171  		authCopy.ServerAddress = ""
   172  		tmpAuthConfigs[k] = authCopy
   173  	}
   174  
   175  	saveAuthConfigs := configFile.AuthConfigs
   176  	configFile.AuthConfigs = tmpAuthConfigs
   177  	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
   178  
   179  	data, err := json.MarshalIndent(configFile, "", "\t")
   180  	if err != nil {
   181  		return err
   182  	}
   183  
   184  	if err := system.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
   185  		return err
   186  	}
   187  
   188  	if err := ioutil.WriteFile(configFile.filename, data, 0600); err != nil {
   189  		return err
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  // Filename returns the name of the configuration file
   196  func (configFile *ConfigFile) Filename() string {
   197  	return configFile.filename
   198  }
   199  
   200  // EncodeAuth creates a base64 encoded string to containing authorization information
   201  func EncodeAuth(authConfig *AuthConfig) string {
   202  	authStr := authConfig.Username + ":" + authConfig.Password
   203  	msg := []byte(authStr)
   204  	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
   205  	base64.StdEncoding.Encode(encoded, msg)
   206  	return string(encoded)
   207  }
   208  
   209  // DecodeAuth decodes a base64 encoded string and returns username and password
   210  func DecodeAuth(authStr string) (string, string, error) {
   211  	decLen := base64.StdEncoding.DecodedLen(len(authStr))
   212  	decoded := make([]byte, decLen)
   213  	authByte := []byte(authStr)
   214  	n, err := base64.StdEncoding.Decode(decoded, authByte)
   215  	if err != nil {
   216  		return "", "", err
   217  	}
   218  	if n > decLen {
   219  		return "", "", fmt.Errorf("Something went wrong decoding auth config")
   220  	}
   221  	arr := strings.SplitN(string(decoded), ":", 2)
   222  	if len(arr) != 2 {
   223  		return "", "", fmt.Errorf("Invalid auth configuration file")
   224  	}
   225  	password := strings.Trim(arr[1], "\x00")
   226  	return arr[0], password, nil
   227  }