github.com/ld86/docker@v1.7.1-rc3/cliconfig/config.go (about) 1 package cliconfig 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 13 "github.com/docker/docker/pkg/homedir" 14 ) 15 16 const ( 17 // Where we store the config file 18 CONFIGFILE = "config.json" 19 OLD_CONFIGFILE = ".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 DEFAULT_INDEXSERVER = "https://index.docker.io/v1/" 25 ) 26 27 var ( 28 ErrConfigFileMissing = errors.New("The Auth config file is missing") 29 ) 30 31 // Registry Auth Info 32 type AuthConfig struct { 33 Username string `json:"username,omitempty"` 34 Password string `json:"password,omitempty"` 35 Auth string `json:"auth"` 36 Email string `json:"email"` 37 ServerAddress string `json:"serveraddress,omitempty"` 38 } 39 40 // ~/.docker/config.json file info 41 type ConfigFile struct { 42 AuthConfigs map[string]AuthConfig `json:"auths"` 43 HttpHeaders map[string]string `json:"HttpHeaders,omitempty"` 44 filename string // Note: not serialized - for internal use only 45 } 46 47 func NewConfigFile(fn string) *ConfigFile { 48 return &ConfigFile{ 49 AuthConfigs: make(map[string]AuthConfig), 50 HttpHeaders: make(map[string]string), 51 filename: fn, 52 } 53 } 54 55 // load up the auth config information and return values 56 // FIXME: use the internal golang config parser 57 func Load(configDir string) (*ConfigFile, error) { 58 if configDir == "" { 59 configDir = filepath.Join(homedir.Get(), ".docker") 60 } 61 62 configFile := ConfigFile{ 63 AuthConfigs: make(map[string]AuthConfig), 64 filename: filepath.Join(configDir, CONFIGFILE), 65 } 66 67 // Try happy path first - latest config file 68 if _, err := os.Stat(configFile.filename); err == nil { 69 file, err := os.Open(configFile.filename) 70 if err != nil { 71 return &configFile, err 72 } 73 defer file.Close() 74 75 if err := json.NewDecoder(file).Decode(&configFile); err != nil { 76 return &configFile, err 77 } 78 79 for addr, ac := range configFile.AuthConfigs { 80 ac.Username, ac.Password, err = DecodeAuth(ac.Auth) 81 if err != nil { 82 return &configFile, err 83 } 84 ac.Auth = "" 85 ac.ServerAddress = addr 86 configFile.AuthConfigs[addr] = ac 87 } 88 89 return &configFile, nil 90 } else if !os.IsNotExist(err) { 91 // if file is there but we can't stat it for any reason other 92 // than it doesn't exist then stop 93 return &configFile, err 94 } 95 96 // Can't find latest config file so check for the old one 97 confFile := filepath.Join(homedir.Get(), OLD_CONFIGFILE) 98 99 if _, err := os.Stat(confFile); err != nil { 100 return &configFile, nil //missing file is not an error 101 } 102 103 b, err := ioutil.ReadFile(confFile) 104 if err != nil { 105 return &configFile, err 106 } 107 108 if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil { 109 arr := strings.Split(string(b), "\n") 110 if len(arr) < 2 { 111 return &configFile, fmt.Errorf("The Auth config file is empty") 112 } 113 authConfig := AuthConfig{} 114 origAuth := strings.Split(arr[0], " = ") 115 if len(origAuth) != 2 { 116 return &configFile, fmt.Errorf("Invalid Auth config file") 117 } 118 authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1]) 119 if err != nil { 120 return &configFile, err 121 } 122 origEmail := strings.Split(arr[1], " = ") 123 if len(origEmail) != 2 { 124 return &configFile, fmt.Errorf("Invalid Auth config file") 125 } 126 authConfig.Email = origEmail[1] 127 authConfig.ServerAddress = DEFAULT_INDEXSERVER 128 configFile.AuthConfigs[DEFAULT_INDEXSERVER] = authConfig 129 } else { 130 for k, authConfig := range configFile.AuthConfigs { 131 authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth) 132 if err != nil { 133 return &configFile, err 134 } 135 authConfig.Auth = "" 136 authConfig.ServerAddress = k 137 configFile.AuthConfigs[k] = authConfig 138 } 139 } 140 return &configFile, nil 141 } 142 143 func (configFile *ConfigFile) Save() error { 144 // Encode sensitive data into a new/temp struct 145 tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs)) 146 for k, authConfig := range configFile.AuthConfigs { 147 authCopy := authConfig 148 149 authCopy.Auth = EncodeAuth(&authCopy) 150 authCopy.Username = "" 151 authCopy.Password = "" 152 authCopy.ServerAddress = "" 153 tmpAuthConfigs[k] = authCopy 154 } 155 156 saveAuthConfigs := configFile.AuthConfigs 157 configFile.AuthConfigs = tmpAuthConfigs 158 defer func() { configFile.AuthConfigs = saveAuthConfigs }() 159 160 data, err := json.MarshalIndent(configFile, "", "\t") 161 if err != nil { 162 return err 163 } 164 165 if err := os.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil { 166 return err 167 } 168 169 if err := ioutil.WriteFile(configFile.filename, data, 0600); err != nil { 170 return err 171 } 172 173 return nil 174 } 175 176 func (config *ConfigFile) Filename() string { 177 return config.filename 178 } 179 180 // create a base64 encoded auth string to store in config 181 func EncodeAuth(authConfig *AuthConfig) string { 182 authStr := authConfig.Username + ":" + authConfig.Password 183 msg := []byte(authStr) 184 encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg))) 185 base64.StdEncoding.Encode(encoded, msg) 186 return string(encoded) 187 } 188 189 // decode the auth string 190 func DecodeAuth(authStr string) (string, string, error) { 191 decLen := base64.StdEncoding.DecodedLen(len(authStr)) 192 decoded := make([]byte, decLen) 193 authByte := []byte(authStr) 194 n, err := base64.StdEncoding.Decode(decoded, authByte) 195 if err != nil { 196 return "", "", err 197 } 198 if n > decLen { 199 return "", "", fmt.Errorf("Something went wrong decoding auth config") 200 } 201 arr := strings.SplitN(string(decoded), ":", 2) 202 if len(arr) != 2 { 203 return "", "", fmt.Errorf("Invalid auth configuration file") 204 } 205 password := strings.Trim(arr[1], "\x00") 206 return arr[0], password, nil 207 }