mirror of https://github.com/docker/docs.git
Merge pull request #751 from dotcloud/660-auth_client-feature
* Registry: Move auth to the client
This commit is contained in:
commit
8085754507
65
api.go
65
api.go
|
@ -13,7 +13,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const APIVERSION = 1.1
|
||||
const APIVERSION = 1.2
|
||||
|
||||
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
|
@ -71,8 +71,10 @@ func getBoolParam(value string) (bool, error) {
|
|||
}
|
||||
|
||||
func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// FIXME: Handle multiple login at once
|
||||
// FIXME: return specific error code if config file missing?
|
||||
if version > 1.1 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return nil
|
||||
}
|
||||
authConfig, err := auth.LoadConfig(srv.runtime.root)
|
||||
if err != nil {
|
||||
if err != auth.ErrConfigFileMissing {
|
||||
|
@ -89,29 +91,34 @@ func getAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reques
|
|||
}
|
||||
|
||||
func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// FIXME: Handle multiple login at once
|
||||
config := &auth.AuthConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
|
||||
authConfig := &auth.AuthConfig{}
|
||||
err := json.NewDecoder(r.Body).Decode(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authConfig, err := auth.LoadConfig(srv.runtime.root)
|
||||
if err != nil {
|
||||
if err != auth.ErrConfigFileMissing {
|
||||
status := ""
|
||||
if version > 1.1 {
|
||||
status, err = auth.Login(authConfig, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authConfig = &auth.AuthConfig{}
|
||||
}
|
||||
if config.Username == authConfig.Username {
|
||||
config.Password = authConfig.Password
|
||||
}
|
||||
} 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(config.Username, config.Password, config.Email, srv.runtime.root)
|
||||
status, err := auth.Login(newAuthConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
newAuthConfig := auth.NewAuthConfig(authConfig.Username, authConfig.Password, authConfig.Email, srv.runtime.root)
|
||||
status, err = auth.Login(newAuthConfig, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if status != "" {
|
||||
b, err := json.Marshal(&APIAuth{Status: status})
|
||||
if err != nil {
|
||||
|
@ -322,7 +329,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht
|
|||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
if image != "" { //pull
|
||||
registry := r.Form.Get("registry")
|
||||
if err := srv.ImagePull(image, tag, registry, w, sf); err != nil {
|
||||
if err := srv.ImagePull(image, tag, registry, w, sf, &auth.AuthConfig{}); err != nil {
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
|
@ -390,6 +397,18 @@ 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 := parseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -403,7 +422,7 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
|
|||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
sf := utils.NewStreamFormatter(version > 1.0)
|
||||
if err := srv.ImagePush(name, registry, w, sf); err != nil {
|
||||
if err := srv.ImagePush(name, registry, w, sf, authConfig); err != nil {
|
||||
if sf.Used() {
|
||||
w.Write(sf.FormatError(err))
|
||||
return nil
|
||||
|
@ -490,7 +509,7 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R
|
|||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
name := vars["name"]
|
||||
imgs, err := srv.ImageDelete(name, version > 1.0)
|
||||
imgs, err := srv.ImageDelete(name, version > 1.1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
43
api_test.go
43
api_test.go
|
@ -6,7 +6,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/registry"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"io"
|
||||
"net"
|
||||
|
@ -18,7 +17,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func TestGetAuth(t *testing.T) {
|
||||
func TestPostAuth(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -54,12 +53,6 @@ func TestGetAuth(t *testing.T) {
|
|||
if r.Code != http.StatusOK && r.Code != 0 {
|
||||
t.Fatalf("%d OK or 0 expected, received %d\n", http.StatusOK, r.Code)
|
||||
}
|
||||
|
||||
newAuthConfig := registry.NewRegistry(runtime.root).GetAuthConfig(false)
|
||||
if newAuthConfig.Username != authConfig.Username ||
|
||||
newAuthConfig.Email != authConfig.Email {
|
||||
t.Fatalf("The auth configuration hasn't been set correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
|
@ -494,40 +487,6 @@ func TestGetContainersByName(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPostAuth(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer nuke(runtime)
|
||||
|
||||
srv := &Server{
|
||||
runtime: runtime,
|
||||
}
|
||||
|
||||
config := &auth.AuthConfig{
|
||||
Username: "utest",
|
||||
Email: "utest@yopmail.com",
|
||||
}
|
||||
|
||||
authStr := auth.EncodeAuth(config)
|
||||
auth.SaveConfig(runtime.root, authStr, config.Email)
|
||||
|
||||
r := httptest.NewRecorder()
|
||||
if err := getAuth(srv, APIVERSION, r, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authConfig := &auth.AuthConfig{}
|
||||
if err := json.Unmarshal(r.Body.Bytes(), authConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if authConfig.Username != config.Username || authConfig.Email != config.Email {
|
||||
t.Errorf("The retrieve auth mismatch with the one set.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostCommit(t *testing.T) {
|
||||
runtime, err := newTestRuntime()
|
||||
if err != nil {
|
||||
|
|
29
auth/auth.go
29
auth/auth.go
|
@ -48,7 +48,7 @@ func IndexServerAddress() string {
|
|||
}
|
||||
|
||||
// create a base64 encoded auth string to store in config
|
||||
func EncodeAuth(authConfig *AuthConfig) string {
|
||||
func encodeAuth(authConfig *AuthConfig) string {
|
||||
authStr := authConfig.Username + ":" + authConfig.Password
|
||||
msg := []byte(authStr)
|
||||
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
|
||||
|
@ -57,7 +57,7 @@ func EncodeAuth(authConfig *AuthConfig) string {
|
|||
}
|
||||
|
||||
// decode the auth string
|
||||
func DecodeAuth(authStr string) (*AuthConfig, error) {
|
||||
func decodeAuth(authStr string) (*AuthConfig, error) {
|
||||
decLen := base64.StdEncoding.DecodedLen(len(authStr))
|
||||
decoded := make([]byte, decLen)
|
||||
authByte := []byte(authStr)
|
||||
|
@ -82,7 +82,7 @@ func DecodeAuth(authStr string) (*AuthConfig, error) {
|
|||
func LoadConfig(rootPath string) (*AuthConfig, error) {
|
||||
confFile := path.Join(rootPath, CONFIGFILE)
|
||||
if _, err := os.Stat(confFile); err != nil {
|
||||
return nil, ErrConfigFileMissing
|
||||
return &AuthConfig{rootPath:rootPath}, ErrConfigFileMissing
|
||||
}
|
||||
b, err := ioutil.ReadFile(confFile)
|
||||
if err != nil {
|
||||
|
@ -94,7 +94,7 @@ func LoadConfig(rootPath string) (*AuthConfig, error) {
|
|||
}
|
||||
origAuth := strings.Split(arr[0], " = ")
|
||||
origEmail := strings.Split(arr[1], " = ")
|
||||
authConfig, err := DecodeAuth(origAuth[1])
|
||||
authConfig, err := decodeAuth(origAuth[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -104,13 +104,13 @@ func LoadConfig(rootPath string) (*AuthConfig, error) {
|
|||
}
|
||||
|
||||
// save the auth config
|
||||
func SaveConfig(rootPath, authStr string, email string) error {
|
||||
confFile := path.Join(rootPath, CONFIGFILE)
|
||||
if len(email) == 0 {
|
||||
func SaveConfig(authConfig *AuthConfig) error {
|
||||
confFile := path.Join(authConfig.rootPath, CONFIGFILE)
|
||||
if len(authConfig.Email) == 0 {
|
||||
os.Remove(confFile)
|
||||
return nil
|
||||
}
|
||||
lines := "auth = " + authStr + "\n" + "email = " + email + "\n"
|
||||
lines := "auth = " + encodeAuth(authConfig) + "\n" + "email = " + authConfig.Email + "\n"
|
||||
b := []byte(lines)
|
||||
err := ioutil.WriteFile(confFile, b, 0600)
|
||||
if err != nil {
|
||||
|
@ -120,7 +120,7 @@ func SaveConfig(rootPath, authStr string, email string) error {
|
|||
}
|
||||
|
||||
// try to register/login to the registry server
|
||||
func Login(authConfig *AuthConfig) (string, error) {
|
||||
func Login(authConfig *AuthConfig, store bool) (string, error) {
|
||||
storeConfig := false
|
||||
client := &http.Client{}
|
||||
reqStatusCode := 0
|
||||
|
@ -168,8 +168,10 @@ func Login(authConfig *AuthConfig) (string, error) {
|
|||
status = "Login Succeeded\n"
|
||||
storeConfig = true
|
||||
} else if resp.StatusCode == 401 {
|
||||
if err := SaveConfig(authConfig.rootPath, "", ""); err != nil {
|
||||
return "", err
|
||||
if store {
|
||||
if err := SaveConfig(authConfig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("Wrong login/password, please try again")
|
||||
} else {
|
||||
|
@ -182,9 +184,8 @@ func Login(authConfig *AuthConfig) (string, error) {
|
|||
} else {
|
||||
return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
|
||||
}
|
||||
if storeConfig {
|
||||
authStr := EncodeAuth(authConfig)
|
||||
if err := SaveConfig(authConfig.rootPath, authStr, authConfig.Email); err != nil {
|
||||
if storeConfig && store {
|
||||
if err := SaveConfig(authConfig); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ func (b *buildFile) CmdFrom(name string) error {
|
|||
remote = name
|
||||
}
|
||||
|
||||
if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false)); err != nil {
|
||||
if err := b.srv.ImagePull(remote, tag, "", b.out, utils.NewStreamFormatter(false), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
88
commands.go
88
commands.go
|
@ -287,27 +287,16 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
body, _, err := cli.call("GET", "/auth", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var out auth.AuthConfig
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var username string
|
||||
var password string
|
||||
var email string
|
||||
|
||||
fmt.Print("Username (", out.Username, "): ")
|
||||
fmt.Print("Username (", cli.authConfig.Username, "): ")
|
||||
username = readAndEchoString(os.Stdin, os.Stdout)
|
||||
if username == "" {
|
||||
username = out.Username
|
||||
username = cli.authConfig.Username
|
||||
}
|
||||
if username != out.Username {
|
||||
if username != cli.authConfig.Username {
|
||||
fmt.Print("Password: ")
|
||||
password = readString(os.Stdin, os.Stdout)
|
||||
|
||||
|
@ -315,20 +304,21 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
|||
return fmt.Errorf("Error : Password Required")
|
||||
}
|
||||
|
||||
fmt.Print("Email (", out.Email, "): ")
|
||||
fmt.Print("Email (", cli.authConfig.Email, "): ")
|
||||
email = readAndEchoString(os.Stdin, os.Stdout)
|
||||
if email == "" {
|
||||
email = out.Email
|
||||
email = cli.authConfig.Email
|
||||
}
|
||||
} else {
|
||||
email = out.Email
|
||||
email = cli.authConfig.Email
|
||||
}
|
||||
term.RestoreTerminal(oldState)
|
||||
|
||||
out.Username = username
|
||||
out.Password = password
|
||||
out.Email = email
|
||||
cli.authConfig.Username = username
|
||||
cli.authConfig.Password = password
|
||||
cli.authConfig.Email = email
|
||||
|
||||
body, _, err = cli.call("POST", "/auth", out)
|
||||
body, _, err := cli.call("POST", "/auth", cli.authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -336,10 +326,11 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
|||
var out2 APIAuth
|
||||
err = json.Unmarshal(body, &out2)
|
||||
if err != nil {
|
||||
auth.LoadConfig(os.Getenv("HOME"))
|
||||
return err
|
||||
}
|
||||
auth.SaveConfig(cli.authConfig)
|
||||
if out2.Status != "" {
|
||||
term.RestoreTerminal(oldState)
|
||||
fmt.Print(out2.Status)
|
||||
}
|
||||
return nil
|
||||
|
@ -724,18 +715,22 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
username, err := cli.checkIfLogged(*registry == "", "push")
|
||||
if err != nil {
|
||||
if err := cli.checkIfLogged(*registry == "", "push"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)", username, name)
|
||||
return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", cli.authConfig.Username, name)
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(cli.authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("registry", *registry)
|
||||
if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, os.Stdout); err != nil {
|
||||
if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -1296,38 +1291,17 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) checkIfLogged(condition bool, action string) (string, error) {
|
||||
body, _, err := cli.call("GET", "/auth", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var out auth.AuthConfig
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (cli *DockerCli) checkIfLogged(condition bool, action string) error {
|
||||
// If condition AND the login failed
|
||||
if condition && out.Username == "" {
|
||||
if condition && cli.authConfig.Username == "" {
|
||||
if err := cli.CmdLogin(""); err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
|
||||
body, _, err = cli.call("GET", "/auth", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = json.Unmarshal(body, &out)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if out.Username == "" {
|
||||
return "", fmt.Errorf("Please login prior to %s. ('docker login')", action)
|
||||
if cli.authConfig.Username == "" {
|
||||
return fmt.Errorf("Please login prior to %s. ('docker login')", action)
|
||||
}
|
||||
}
|
||||
return out.Username, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) {
|
||||
|
@ -1514,10 +1488,12 @@ func Subcmd(name, signature, description string) *flag.FlagSet {
|
|||
}
|
||||
|
||||
func NewDockerCli(addr string, port int) *DockerCli {
|
||||
return &DockerCli{addr, port}
|
||||
authConfig, _ := auth.LoadConfig(os.Getenv("HOME"))
|
||||
return &DockerCli{addr, port, authConfig}
|
||||
}
|
||||
|
||||
type DockerCli struct {
|
||||
host string
|
||||
port int
|
||||
host string
|
||||
port int
|
||||
authConfig *auth.AuthConfig
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -473,10 +473,7 @@ type Registry struct {
|
|||
authConfig *auth.AuthConfig
|
||||
}
|
||||
|
||||
func NewRegistry(root string) *Registry {
|
||||
// If the auth file does not exist, keep going
|
||||
authConfig, _ := auth.LoadConfig(root)
|
||||
|
||||
func NewRegistry(root string, authConfig *auth.AuthConfig) *Registry {
|
||||
httpTransport := &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
|
|
|
@ -68,7 +68,7 @@ func init() {
|
|||
runtime: runtime,
|
||||
}
|
||||
// Retrieve the Image
|
||||
if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false)); err != nil {
|
||||
if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false), nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
10
server.go
10
server.go
|
@ -55,7 +55,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
|
|||
|
||||
func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
|
||||
|
||||
results, err := registry.NewRegistry(srv.runtime.root).SearchRepositories(term)
|
||||
results, err := registry.NewRegistry(srv.runtime.root, nil).SearchRepositories(term)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -395,8 +395,8 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, local, re
|
|||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
|
||||
r := registry.NewRegistry(srv.runtime.root)
|
||||
func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
|
||||
r := registry.NewRegistry(srv.runtime.root, authConfig)
|
||||
out = utils.NewWriteFlusher(out)
|
||||
if endpoint != "" {
|
||||
if err := srv.pullImage(r, out, name, endpoint, nil, sf); err != nil {
|
||||
|
@ -587,10 +587,10 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter) error {
|
||||
func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
|
||||
out = utils.NewWriteFlusher(out)
|
||||
img, err := srv.runtime.graph.Get(name)
|
||||
r := registry.NewRegistry(srv.runtime.root)
|
||||
r := registry.NewRegistry(srv.runtime.root, authConfig)
|
||||
|
||||
if err != nil {
|
||||
out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", name, len(srv.runtime.repositories.Repositories[name])))
|
||||
|
|
Loading…
Reference in New Issue