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 }