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 }