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