Bladeren bron

change dockercfg to json and support multiple auth remote

Victor Vieux 12 jaren geleden
bovenliggende
commit
3bae188b8d
3 gewijzigde bestanden met toevoegingen van 87 en 127 verwijderingen
  1. 5 53
      api.go
  2. 54 50
      auth/auth.go
  3. 28 24
      commands.go

+ 5 - 53
api.go

@@ -81,54 +81,15 @@ func getBoolParam(value string) (bool, error) {
 	return ret, nil
 }
 
-func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
-	if version > 1.1 {
-		w.WriteHeader(http.StatusNotFound)
-		return nil
-	}
-	authConfig, err := auth.LoadConfig(srv.runtime.root)
-	if err != nil {
-		if err != auth.ErrConfigFileMissing {
-			return err
-		}
-		authConfig = &auth.AuthConfig{}
-	}
-	b, err := json.Marshal(&auth.AuthConfig{Username: authConfig.Username, Email: authConfig.Email})
-	if err != nil {
-		return err
-	}
-	writeJSON(w, b)
-	return nil
-}
-
 func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	authConfig := &auth.AuthConfig{}
 	err := json.NewDecoder(r.Body).Decode(authConfig)
 	if err != nil {
 		return err
 	}
-	status := ""
-	if version > 1.1 {
-		status, err = auth.Login(authConfig, false)
-		if err != nil {
-			return err
-		}
-	} else {
-		localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
-		if err != nil {
-			if err != auth.ErrConfigFileMissing {
-				return err
-			}
-		}
-		if authConfig.Username == localAuthConfig.Username {
-			authConfig.Password = localAuthConfig.Password
-		}
-
-		newAuthConfig := auth.NewAuthConfig(authConfig.Username, authConfig.Password, authConfig.Email, srv.runtime.root)
-		status, err = auth.Login(newAuthConfig, true)
-		if err != nil {
-			return err
-		}
+	status, err := auth.Login(authConfig)
+	if err != nil {
+		return err
 	}
 	if status != "" {
 		b, err := json.Marshal(&APIAuth{Status: status})
@@ -429,16 +390,8 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht
 
 func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	authConfig := &auth.AuthConfig{}
-	if version > 1.1 {
-		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
-			return err
-		}
-	} else {
-		localAuthConfig, err := auth.LoadConfig(srv.runtime.root)
-		if err != nil && err != auth.ErrConfigFileMissing {
-			return err
-		}
-		authConfig = localAuthConfig
+	if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
+		return err
 	}
 	if err := parseForm(r); err != nil {
 		return err
@@ -854,7 +807,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
 
 	m := map[string]map[string]func(*Server, float64, http.ResponseWriter, *http.Request, map[string]string) error{
 		"GET": {
-			"/auth":                         getAuth,
 			"/version":                      getVersion,
 			"/info":                         getInfo,
 			"/images/json":                  getImagesJSON,

+ 54 - 50
auth/auth.go

@@ -25,19 +25,15 @@ var (
 )
 
 type AuthConfig struct {
-	Username string `json:"username"`
-	Password string `json:"password"`
+	Username string `json:"username,omitempty"`
+	Password string `json:"password,omitempty"`
+	Auth     string `json:"auth"`
 	Email    string `json:"email"`
-	rootPath string
 }
 
-func NewAuthConfig(username, password, email, rootPath string) *AuthConfig {
-	return &AuthConfig{
-		Username: username,
-		Password: password,
-		Email:    email,
-		rootPath: rootPath,
-	}
+type ConfigFile struct {
+	Configs  map[string]AuthConfig `json:"configs,omitempty"`
+	rootPath string
 }
 
 func IndexServerAddress() string {
@@ -54,61 +50,83 @@ func encodeAuth(authConfig *AuthConfig) string {
 }
 
 // decode the auth string
-func decodeAuth(authStr string) (*AuthConfig, error) {
+func decodeAuth(authStr string) (string, string, error) {
 	decLen := base64.StdEncoding.DecodedLen(len(authStr))
 	decoded := make([]byte, decLen)
 	authByte := []byte(authStr)
 	n, err := base64.StdEncoding.Decode(decoded, authByte)
 	if err != nil {
-		return nil, err
+		return "", "", err
 	}
 	if n > decLen {
-		return nil, fmt.Errorf("Something went wrong decoding auth config")
+		return "", "", fmt.Errorf("Something went wrong decoding auth config")
 	}
 	arr := strings.Split(string(decoded), ":")
 	if len(arr) != 2 {
-		return nil, fmt.Errorf("Invalid auth configuration file")
+		return "", "", fmt.Errorf("Invalid auth configuration file")
 	}
 	password := strings.Trim(arr[1], "\x00")
-	return &AuthConfig{Username: arr[0], Password: password}, nil
+	return arr[0], password, nil
 }
 
 // load up the auth config information and return values
 // FIXME: use the internal golang config parser
-func LoadConfig(rootPath string) (*AuthConfig, error) {
+func LoadConfig(rootPath string) (*ConfigFile, error) {
+	configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
 	confFile := path.Join(rootPath, CONFIGFILE)
 	if _, err := os.Stat(confFile); err != nil {
-		return &AuthConfig{rootPath: rootPath}, ErrConfigFileMissing
+		return &configFile, ErrConfigFileMissing
 	}
 	b, err := ioutil.ReadFile(confFile)
 	if err != nil {
 		return nil, err
 	}
-	arr := strings.Split(string(b), "\n")
-	if len(arr) < 2 {
-		return nil, fmt.Errorf("The Auth config file is empty")
-	}
-	origAuth := strings.Split(arr[0], " = ")
-	origEmail := strings.Split(arr[1], " = ")
-	authConfig, err := decodeAuth(origAuth[1])
-	if err != nil {
-		return nil, err
+
+	if err := json.Unmarshal(b, &configFile.Configs); err != nil {
+		arr := strings.Split(string(b), "\n")
+		if len(arr) < 2 {
+			return nil, fmt.Errorf("The Auth config file is empty")
+		}
+		authConfig := AuthConfig{}
+		origAuth := strings.Split(arr[0], " = ")
+		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
+		if err != nil {
+			return nil, err
+		}
+		origEmail := strings.Split(arr[1], " = ")
+		authConfig.Email = origEmail[1]
+		configFile.Configs[IndexServerAddress()] = authConfig
+	} else {
+		for k, authConfig := range configFile.Configs {
+			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
+			if err != nil {
+				return nil, err
+			}
+			configFile.Configs[k] = authConfig
+		}
 	}
-	authConfig.Email = origEmail[1]
-	authConfig.rootPath = rootPath
-	return authConfig, nil
+	return &configFile, nil
 }
 
 // save the auth config
-func SaveConfig(authConfig *AuthConfig) error {
-	confFile := path.Join(authConfig.rootPath, CONFIGFILE)
-	if len(authConfig.Email) == 0 {
+func SaveConfig(configFile *ConfigFile) error {
+	confFile := path.Join(configFile.rootPath, CONFIGFILE)
+	if len(configFile.Configs) == 0 {
 		os.Remove(confFile)
 		return nil
 	}
-	lines := "auth = " + encodeAuth(authConfig) + "\n" + "email = " + authConfig.Email + "\n"
-	b := []byte(lines)
-	err := ioutil.WriteFile(confFile, b, 0600)
+	for k, authConfig := range configFile.Configs {
+		authConfig.Auth = encodeAuth(&authConfig)
+		authConfig.Username = ""
+		authConfig.Password = ""
+		configFile.Configs[k] = authConfig
+	}
+
+	b, err := json.Marshal(configFile.Configs)
+	if err != nil {
+		return err
+	}
+	err = ioutil.WriteFile(confFile, b, 0600)
 	if err != nil {
 		return err
 	}
@@ -116,8 +134,7 @@ func SaveConfig(authConfig *AuthConfig) error {
 }
 
 // try to register/login to the registry server
-func Login(authConfig *AuthConfig, store bool) (string, error) {
-	storeConfig := false
+func Login(authConfig *AuthConfig) (string, error) {
 	client := &http.Client{}
 	reqStatusCode := 0
 	var status string
@@ -143,7 +160,6 @@ func Login(authConfig *AuthConfig, store bool) (string, error) {
 	if reqStatusCode == 201 {
 		status = "Account created. Please use the confirmation link we sent" +
 			" to your e-mail to activate it."
-		storeConfig = true
 	} else if reqStatusCode == 403 {
 		return "", fmt.Errorf("Login: Your account hasn't been activated. " +
 			"Please check your e-mail for a confirmation link.")
@@ -162,14 +178,7 @@ func Login(authConfig *AuthConfig, store bool) (string, error) {
 			}
 			if resp.StatusCode == 200 {
 				status = "Login Succeeded"
-				storeConfig = true
 			} else if resp.StatusCode == 401 {
-				if store {
-					authConfig.Email = ""
-					if err := SaveConfig(authConfig); err != nil {
-						return "", err
-					}
-				}
 				return "", fmt.Errorf("Wrong login/password, please try again")
 			} else {
 				return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
@@ -181,10 +190,5 @@ func Login(authConfig *AuthConfig, store bool) (string, error) {
 	} else {
 		return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
 	}
-	if storeConfig && store {
-		if err := SaveConfig(authConfig); err != nil {
-			return "", err
-		}
-	}
 	return status, nil
 }

+ 28 - 24
commands.go

@@ -313,16 +313,21 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 		email    string
 	)
 
+	authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()]
+	if !ok {
+		authconfig = auth.AuthConfig{}
+	}
+
 	if *flUsername == "" {
-		fmt.Fprintf(cli.out, "Username (%s): ", cli.authConfig.Username)
+		fmt.Fprintf(cli.out, "Username (%s): ", authconfig.Username)
 		username = readAndEchoString(cli.in, cli.out)
 		if username == "" {
-			username = cli.authConfig.Username
+			username = authconfig.Username
 		}
 	} else {
 		username = *flUsername
 	}
-	if username != cli.authConfig.Username {
+	if username != authconfig.Username {
 		if *flPassword == "" {
 			fmt.Fprintf(cli.out, "Password: ")
 			password = readString(cli.in, cli.out)
@@ -334,31 +339,30 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 		}
 
 		if *flEmail == "" {
-			fmt.Fprintf(cli.out, "Email (%s): ", cli.authConfig.Email)
+			fmt.Fprintf(cli.out, "Email (%s): ", authconfig.Email)
 			email = readAndEchoString(cli.in, cli.out)
 			if email == "" {
-				email = cli.authConfig.Email
+				email = authconfig.Email
 			}
 		} else {
 			email = *flEmail
 		}
 	} else {
-		password = cli.authConfig.Password
-		email = cli.authConfig.Email
+		password = authconfig.Password
+		email = authconfig.Email
 	}
 	if oldState != nil {
 		term.RestoreTerminal(cli.terminalFd, oldState)
 	}
-	cli.authConfig.Username = username
-	cli.authConfig.Password = password
-	cli.authConfig.Email = email
+	authconfig.Username = username
+	authconfig.Password = password
+	authconfig.Email = email
+	cli.configFile.Configs[auth.IndexServerAddress()] = authconfig
 
-	body, statusCode, err := cli.call("POST", "/auth", cli.authConfig)
+	body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[auth.IndexServerAddress()])
 	if statusCode == 401 {
-		cli.authConfig.Username = ""
-		cli.authConfig.Password = ""
-		cli.authConfig.Email = ""
-		auth.SaveConfig(cli.authConfig)
+		delete(cli.configFile.Configs, auth.IndexServerAddress())
+		auth.SaveConfig(cli.configFile)
 		return err
 	}
 	if err != nil {
@@ -368,10 +372,10 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 	var out2 APIAuth
 	err = json.Unmarshal(body, &out2)
 	if err != nil {
-		auth.LoadConfig(os.Getenv("HOME"))
+		cli.configFile, _ = auth.LoadConfig(os.Getenv("HOME"))
 		return err
 	}
-	auth.SaveConfig(cli.authConfig)
+	auth.SaveConfig(cli.configFile)
 	if out2.Status != "" {
 		fmt.Fprintf(cli.out, "%s\n", out2.Status)
 	}
@@ -802,10 +806,10 @@ func (cli *DockerCli) CmdPush(args ...string) error {
 	// Custom repositories can have different rules, and we must also
 	// allow pushing by image ID.
 	if len(strings.SplitN(name, "/", 2)) == 1 {
-		return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", cli.authConfig.Username, name)
+		return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", cli.configFile.Configs[auth.IndexServerAddress()].Username, name)
 	}
 
-	buf, err := json.Marshal(cli.authConfig)
+	buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()])
 	if err != nil {
 		return err
 	}
@@ -1410,11 +1414,11 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 
 func (cli *DockerCli) checkIfLogged(action string) error {
 	// If condition AND the login failed
-	if cli.authConfig.Username == "" {
+	if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" {
 		if err := cli.CmdLogin(""); err != nil {
 			return err
 		}
-		if cli.authConfig.Username == "" {
+		if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" {
 			return fmt.Errorf("Please login prior to %s. ('docker login')", action)
 		}
 	}
@@ -1670,11 +1674,11 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *Doc
 		err = out
 	}
 
-	authConfig, _ := auth.LoadConfig(os.Getenv("HOME"))
+	configFile, _ := auth.LoadConfig(os.Getenv("HOME"))
 	return &DockerCli{
 		proto:      proto,
 		addr:       addr,
-		authConfig: authConfig,
+		configFile: configFile,
 		in:         in,
 		out:        out,
 		err:        err,
@@ -1686,7 +1690,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *Doc
 type DockerCli struct {
 	proto      string
 	addr       string
-	authConfig *auth.AuthConfig
+	configFile *auth.ConfigFile
 	in         io.ReadCloser
 	out        io.Writer
 	err        io.Writer