Browse code

Merge auth package within registry

Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)

Guillaume J. Charmes authored on 2014/03/11 09:16:58
Showing 12 changed files
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"errors"
9 9
 	"fmt"
10 10
 	"github.com/dotcloud/docker/archive"
11
-	"github.com/dotcloud/docker/auth"
12 11
 	"github.com/dotcloud/docker/dockerversion"
13 12
 	"github.com/dotcloud/docker/engine"
14 13
 	"github.com/dotcloud/docker/nat"
... ...
@@ -229,7 +228,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
229 229
 
230 230
 // 'docker login': login / register a user to registry service.
231 231
 func (cli *DockerCli) CmdLogin(args ...string) error {
232
-	cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+auth.IndexServerAddress()+"\" is the default.")
232
+	cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
233 233
 
234 234
 	var username, password, email string
235 235
 
... ...
@@ -240,7 +239,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
240 240
 	if err != nil {
241 241
 		return nil
242 242
 	}
243
-	serverAddress := auth.IndexServerAddress()
243
+	serverAddress := registry.IndexServerAddress()
244 244
 	if len(cmd.Args()) > 0 {
245 245
 		serverAddress = cmd.Arg(0)
246 246
 	}
... ...
@@ -266,7 +265,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
266 266
 	cli.LoadConfigFile()
267 267
 	authconfig, ok := cli.configFile.Configs[serverAddress]
268 268
 	if !ok {
269
-		authconfig = auth.AuthConfig{}
269
+		authconfig = registry.AuthConfig{}
270 270
 	}
271 271
 
272 272
 	if username == "" {
... ...
@@ -311,7 +310,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
311 311
 	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
312 312
 	if statusCode == 401 {
313 313
 		delete(cli.configFile.Configs, serverAddress)
314
-		auth.SaveConfig(cli.configFile)
314
+		registry.SaveConfig(cli.configFile)
315 315
 		return err
316 316
 	}
317 317
 	if err != nil {
... ...
@@ -320,10 +319,10 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
320 320
 	var out2 engine.Env
321 321
 	err = out2.Decode(stream)
322 322
 	if err != nil {
323
-		cli.configFile, _ = auth.LoadConfig(os.Getenv("HOME"))
323
+		cli.configFile, _ = registry.LoadConfig(os.Getenv("HOME"))
324 324
 		return err
325 325
 	}
326
-	auth.SaveConfig(cli.configFile)
326
+	registry.SaveConfig(cli.configFile)
327 327
 	if out2.Get("Status") != "" {
328 328
 		fmt.Fprintf(cli.out, "%s\n", out2.Get("Status"))
329 329
 	}
... ...
@@ -1008,7 +1007,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
1008 1008
 	// Custom repositories can have different rules, and we must also
1009 1009
 	// allow pushing by image ID.
1010 1010
 	if len(strings.SplitN(name, "/", 2)) == 1 {
1011
-		username := cli.configFile.Configs[auth.IndexServerAddress()].Username
1011
+		username := cli.configFile.Configs[registry.IndexServerAddress()].Username
1012 1012
 		if username == "" {
1013 1013
 			username = "<user>"
1014 1014
 		}
... ...
@@ -1016,7 +1015,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
1016 1016
 	}
1017 1017
 
1018 1018
 	v := url.Values{}
1019
-	push := func(authConfig auth.AuthConfig) error {
1019
+	push := func(authConfig registry.AuthConfig) error {
1020 1020
 		buf, err := json.Marshal(authConfig)
1021 1021
 		if err != nil {
1022 1022
 			return err
... ...
@@ -1075,7 +1074,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
1075 1075
 	v.Set("fromImage", remote)
1076 1076
 	v.Set("tag", *tag)
1077 1077
 
1078
-	pull := func(authConfig auth.AuthConfig) error {
1078
+	pull := func(authConfig registry.AuthConfig) error {
1079 1079
 		buf, err := json.Marshal(authConfig)
1080 1080
 		if err != nil {
1081 1081
 			return err
... ...
@@ -2058,8 +2057,8 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
2058 2058
 	if passAuthInfo {
2059 2059
 		cli.LoadConfigFile()
2060 2060
 		// Resolve the Auth config relevant for this server
2061
-		authConfig := cli.configFile.ResolveAuthConfig(auth.IndexServerAddress())
2062
-		getHeaders := func(authConfig auth.AuthConfig) (map[string][]string, error) {
2061
+		authConfig := cli.configFile.ResolveAuthConfig(registry.IndexServerAddress())
2062
+		getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
2063 2063
 			buf, err := json.Marshal(authConfig)
2064 2064
 			if err != nil {
2065 2065
 				return nil, err
... ...
@@ -2340,7 +2339,7 @@ func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet
2340 2340
 }
2341 2341
 
2342 2342
 func (cli *DockerCli) LoadConfigFile() (err error) {
2343
-	cli.configFile, err = auth.LoadConfig(os.Getenv("HOME"))
2343
+	cli.configFile, err = registry.LoadConfig(os.Getenv("HOME"))
2344 2344
 	if err != nil {
2345 2345
 		fmt.Fprintf(cli.err, "WARNING: %s\n", err)
2346 2346
 	}
... ...
@@ -2422,7 +2421,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *Doc
2422 2422
 type DockerCli struct {
2423 2423
 	proto      string
2424 2424
 	addr       string
2425
-	configFile *auth.ConfigFile
2425
+	configFile *registry.ConfigFile
2426 2426
 	in         io.ReadCloser
2427 2427
 	out        io.Writer
2428 2428
 	err        io.Writer
... ...
@@ -8,12 +8,12 @@ import (
8 8
 	"encoding/json"
9 9
 	"expvar"
10 10
 	"fmt"
11
-	"github.com/dotcloud/docker/auth"
12 11
 	"github.com/dotcloud/docker/engine"
13 12
 	"github.com/dotcloud/docker/pkg/listenbuffer"
14 13
 	"github.com/dotcloud/docker/pkg/systemd"
15 14
 	"github.com/dotcloud/docker/pkg/user"
16 15
 	"github.com/dotcloud/docker/pkg/version"
16
+	"github.com/dotcloud/docker/registry"
17 17
 	"github.com/dotcloud/docker/utils"
18 18
 	"github.com/gorilla/mux"
19 19
 	"io"
... ...
@@ -381,13 +381,13 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon
381 381
 		job   *engine.Job
382 382
 	)
383 383
 	authEncoded := r.Header.Get("X-Registry-Auth")
384
-	authConfig := &auth.AuthConfig{}
384
+	authConfig := &registry.AuthConfig{}
385 385
 	if authEncoded != "" {
386 386
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
387 387
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
388 388
 			// for a pull it is not an error if no auth was given
389 389
 			// to increase compatibility with the existing api it is defaulting to be empty
390
-			authConfig = &auth.AuthConfig{}
390
+			authConfig = &registry.AuthConfig{}
391 391
 		}
392 392
 	}
393 393
 	if image != "" { //pull
... ...
@@ -429,7 +429,7 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons
429 429
 	}
430 430
 	var (
431 431
 		authEncoded = r.Header.Get("X-Registry-Auth")
432
-		authConfig  = &auth.AuthConfig{}
432
+		authConfig  = &registry.AuthConfig{}
433 433
 		metaHeaders = map[string][]string{}
434 434
 	)
435 435
 
... ...
@@ -438,7 +438,7 @@ func getImagesSearch(eng *engine.Engine, version version.Version, w http.Respons
438 438
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
439 439
 			// for a search it is not an error if no auth was given
440 440
 			// to increase compatibility with the existing api it is defaulting to be empty
441
-			authConfig = &auth.AuthConfig{}
441
+			authConfig = &registry.AuthConfig{}
442 442
 		}
443 443
 	}
444 444
 	for k, v := range r.Header {
... ...
@@ -494,7 +494,7 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response
494 494
 	if err := parseForm(r); err != nil {
495 495
 		return err
496 496
 	}
497
-	authConfig := &auth.AuthConfig{}
497
+	authConfig := &registry.AuthConfig{}
498 498
 
499 499
 	authEncoded := r.Header.Get("X-Registry-Auth")
500 500
 	if authEncoded != "" {
... ...
@@ -502,7 +502,7 @@ func postImagesPush(eng *engine.Engine, version version.Version, w http.Response
502 502
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
503 503
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
504 504
 			// to increase compatibility to existing api it is defaulting to be empty
505
-			authConfig = &auth.AuthConfig{}
505
+			authConfig = &registry.AuthConfig{}
506 506
 		}
507 507
 	} else {
508 508
 		// the old format is supported for compatibility if there was no authConfig header
... ...
@@ -823,9 +823,9 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
823 823
 	}
824 824
 	var (
825 825
 		authEncoded       = r.Header.Get("X-Registry-Auth")
826
-		authConfig        = &auth.AuthConfig{}
826
+		authConfig        = &registry.AuthConfig{}
827 827
 		configFileEncoded = r.Header.Get("X-Registry-Config")
828
-		configFile        = &auth.ConfigFile{}
828
+		configFile        = &registry.ConfigFile{}
829 829
 		job               = eng.Job("build")
830 830
 	)
831 831
 
... ...
@@ -838,7 +838,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
838 838
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
839 839
 			// for a pull it is not an error if no auth was given
840 840
 			// to increase compatibility with the existing api it is defaulting to be empty
841
-			authConfig = &auth.AuthConfig{}
841
+			authConfig = &registry.AuthConfig{}
842 842
 		}
843 843
 	}
844 844
 
... ...
@@ -847,7 +847,7 @@ func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWrite
847 847
 		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
848 848
 			// for a pull it is not an error if no auth was given
849 849
 			// to increase compatibility with the existing api it is defaulting to be empty
850
-			configFile = &auth.ConfigFile{}
850
+			configFile = &registry.ConfigFile{}
851 851
 		}
852 852
 	}
853 853
 
854 854
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-Sam Alba <sam@dotcloud.com> (@samalba)
2
-Joffrey Fuhrer <joffrey@dotcloud.com> (@shin-)
3
-Ken Cochrane <ken@dotcloud.com> (@kencochrane)
4 1
deleted file mode 100644
... ...
@@ -1,290 +0,0 @@
1
-package auth
2
-
3
-import (
4
-	"encoding/base64"
5
-	"encoding/json"
6
-	"errors"
7
-	"fmt"
8
-	"github.com/dotcloud/docker/utils"
9
-	"io/ioutil"
10
-	"net/http"
11
-	"os"
12
-	"path"
13
-	"strings"
14
-)
15
-
16
-// Where we store the config file
17
-const CONFIGFILE = ".dockercfg"
18
-
19
-// Only used for user auth + account creation
20
-const INDEXSERVER = "https://index.docker.io/v1/"
21
-
22
-//const INDEXSERVER = "https://indexstaging-docker.dotcloud.com/v1/"
23
-
24
-var (
25
-	ErrConfigFileMissing = errors.New("The Auth config file is missing")
26
-)
27
-
28
-type AuthConfig struct {
29
-	Username      string `json:"username,omitempty"`
30
-	Password      string `json:"password,omitempty"`
31
-	Auth          string `json:"auth"`
32
-	Email         string `json:"email"`
33
-	ServerAddress string `json:"serveraddress,omitempty"`
34
-}
35
-
36
-type ConfigFile struct {
37
-	Configs  map[string]AuthConfig `json:"configs,omitempty"`
38
-	rootPath string
39
-}
40
-
41
-func IndexServerAddress() string {
42
-	return INDEXSERVER
43
-}
44
-
45
-// create a base64 encoded auth string to store in config
46
-func encodeAuth(authConfig *AuthConfig) string {
47
-	authStr := authConfig.Username + ":" + authConfig.Password
48
-	msg := []byte(authStr)
49
-	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
50
-	base64.StdEncoding.Encode(encoded, msg)
51
-	return string(encoded)
52
-}
53
-
54
-// decode the auth string
55
-func decodeAuth(authStr string) (string, string, error) {
56
-	decLen := base64.StdEncoding.DecodedLen(len(authStr))
57
-	decoded := make([]byte, decLen)
58
-	authByte := []byte(authStr)
59
-	n, err := base64.StdEncoding.Decode(decoded, authByte)
60
-	if err != nil {
61
-		return "", "", err
62
-	}
63
-	if n > decLen {
64
-		return "", "", fmt.Errorf("Something went wrong decoding auth config")
65
-	}
66
-	arr := strings.SplitN(string(decoded), ":", 2)
67
-	if len(arr) != 2 {
68
-		return "", "", fmt.Errorf("Invalid auth configuration file")
69
-	}
70
-	password := strings.Trim(arr[1], "\x00")
71
-	return arr[0], password, nil
72
-}
73
-
74
-// load up the auth config information and return values
75
-// FIXME: use the internal golang config parser
76
-func LoadConfig(rootPath string) (*ConfigFile, error) {
77
-	configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
78
-	confFile := path.Join(rootPath, CONFIGFILE)
79
-	if _, err := os.Stat(confFile); err != nil {
80
-		return &configFile, nil //missing file is not an error
81
-	}
82
-	b, err := ioutil.ReadFile(confFile)
83
-	if err != nil {
84
-		return &configFile, err
85
-	}
86
-
87
-	if err := json.Unmarshal(b, &configFile.Configs); err != nil {
88
-		arr := strings.Split(string(b), "\n")
89
-		if len(arr) < 2 {
90
-			return &configFile, fmt.Errorf("The Auth config file is empty")
91
-		}
92
-		authConfig := AuthConfig{}
93
-		origAuth := strings.Split(arr[0], " = ")
94
-		if len(origAuth) != 2 {
95
-			return &configFile, fmt.Errorf("Invalid Auth config file")
96
-		}
97
-		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
98
-		if err != nil {
99
-			return &configFile, err
100
-		}
101
-		origEmail := strings.Split(arr[1], " = ")
102
-		if len(origEmail) != 2 {
103
-			return &configFile, fmt.Errorf("Invalid Auth config file")
104
-		}
105
-		authConfig.Email = origEmail[1]
106
-		authConfig.ServerAddress = IndexServerAddress()
107
-		configFile.Configs[IndexServerAddress()] = authConfig
108
-	} else {
109
-		for k, authConfig := range configFile.Configs {
110
-			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
111
-			if err != nil {
112
-				return &configFile, err
113
-			}
114
-			authConfig.Auth = ""
115
-			configFile.Configs[k] = authConfig
116
-			authConfig.ServerAddress = k
117
-		}
118
-	}
119
-	return &configFile, nil
120
-}
121
-
122
-// save the auth config
123
-func SaveConfig(configFile *ConfigFile) error {
124
-	confFile := path.Join(configFile.rootPath, CONFIGFILE)
125
-	if len(configFile.Configs) == 0 {
126
-		os.Remove(confFile)
127
-		return nil
128
-	}
129
-
130
-	configs := make(map[string]AuthConfig, len(configFile.Configs))
131
-	for k, authConfig := range configFile.Configs {
132
-		authCopy := authConfig
133
-
134
-		authCopy.Auth = encodeAuth(&authCopy)
135
-		authCopy.Username = ""
136
-		authCopy.Password = ""
137
-		authCopy.ServerAddress = ""
138
-		configs[k] = authCopy
139
-	}
140
-
141
-	b, err := json.Marshal(configs)
142
-	if err != nil {
143
-		return err
144
-	}
145
-	err = ioutil.WriteFile(confFile, b, 0600)
146
-	if err != nil {
147
-		return err
148
-	}
149
-	return nil
150
-}
151
-
152
-// try to register/login to the registry server
153
-func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
154
-	var (
155
-		status        string
156
-		reqBody       []byte
157
-		err           error
158
-		client        = &http.Client{}
159
-		reqStatusCode = 0
160
-		serverAddress = authConfig.ServerAddress
161
-	)
162
-
163
-	if serverAddress == "" {
164
-		serverAddress = IndexServerAddress()
165
-	}
166
-
167
-	loginAgainstOfficialIndex := serverAddress == IndexServerAddress()
168
-
169
-	// to avoid sending the server address to the server it should be removed before being marshalled
170
-	authCopy := *authConfig
171
-	authCopy.ServerAddress = ""
172
-
173
-	jsonBody, err := json.Marshal(authCopy)
174
-	if err != nil {
175
-		return "", fmt.Errorf("Config Error: %s", err)
176
-	}
177
-
178
-	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
179
-	b := strings.NewReader(string(jsonBody))
180
-	req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
181
-	if err != nil {
182
-		return "", fmt.Errorf("Server Error: %s", err)
183
-	}
184
-	reqStatusCode = req1.StatusCode
185
-	defer req1.Body.Close()
186
-	reqBody, err = ioutil.ReadAll(req1.Body)
187
-	if err != nil {
188
-		return "", fmt.Errorf("Server Error: [%#v] %s", reqStatusCode, err)
189
-	}
190
-
191
-	if reqStatusCode == 201 {
192
-		if loginAgainstOfficialIndex {
193
-			status = "Account created. Please use the confirmation link we sent" +
194
-				" to your e-mail to activate it."
195
-		} else {
196
-			status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
197
-		}
198
-	} else if reqStatusCode == 400 {
199
-		if string(reqBody) == "\"Username or email already exists\"" {
200
-			req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
201
-			req.SetBasicAuth(authConfig.Username, authConfig.Password)
202
-			resp, err := client.Do(req)
203
-			if err != nil {
204
-				return "", err
205
-			}
206
-			defer resp.Body.Close()
207
-			body, err := ioutil.ReadAll(resp.Body)
208
-			if err != nil {
209
-				return "", err
210
-			}
211
-			if resp.StatusCode == 200 {
212
-				status = "Login Succeeded"
213
-			} else if resp.StatusCode == 401 {
214
-				return "", fmt.Errorf("Wrong login/password, please try again")
215
-			} else if resp.StatusCode == 403 {
216
-				if loginAgainstOfficialIndex {
217
-					return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
218
-				}
219
-				return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
220
-			} else {
221
-				return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
222
-			}
223
-		} else {
224
-			return "", fmt.Errorf("Registration: %s", reqBody)
225
-		}
226
-	} else if reqStatusCode == 401 {
227
-		// This case would happen with private registries where /v1/users is
228
-		// protected, so people can use `docker login` as an auth check.
229
-		req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
230
-		req.SetBasicAuth(authConfig.Username, authConfig.Password)
231
-		resp, err := client.Do(req)
232
-		if err != nil {
233
-			return "", err
234
-		}
235
-		defer resp.Body.Close()
236
-		body, err := ioutil.ReadAll(resp.Body)
237
-		if err != nil {
238
-			return "", err
239
-		}
240
-		if resp.StatusCode == 200 {
241
-			status = "Login Succeeded"
242
-		} else if resp.StatusCode == 401 {
243
-			return "", fmt.Errorf("Wrong login/password, please try again")
244
-		} else {
245
-			return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
246
-				resp.StatusCode, resp.Header)
247
-		}
248
-	} else {
249
-		return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
250
-	}
251
-	return status, nil
252
-}
253
-
254
-// this method matches a auth configuration to a server address or a url
255
-func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
256
-	if hostname == IndexServerAddress() || len(hostname) == 0 {
257
-		// default to the index server
258
-		return config.Configs[IndexServerAddress()]
259
-	}
260
-
261
-	// First try the happy case
262
-	if c, found := config.Configs[hostname]; found {
263
-		return c
264
-	}
265
-
266
-	convertToHostname := func(url string) string {
267
-		stripped := url
268
-		if strings.HasPrefix(url, "http://") {
269
-			stripped = strings.Replace(url, "http://", "", 1)
270
-		} else if strings.HasPrefix(url, "https://") {
271
-			stripped = strings.Replace(url, "https://", "", 1)
272
-		}
273
-
274
-		nameParts := strings.SplitN(stripped, "/", 2)
275
-
276
-		return nameParts[0]
277
-	}
278
-
279
-	// Maybe they have a legacy config file, we will iterate the keys converting
280
-	// them to the new format and testing
281
-	normalizedHostename := convertToHostname(hostname)
282
-	for registry, config := range config.Configs {
283
-		if registryHostname := convertToHostname(registry); registryHostname == normalizedHostename {
284
-			return config
285
-		}
286
-	}
287
-
288
-	// When all else fails, return an empty auth config
289
-	return AuthConfig{}
290
-}
291 1
deleted file mode 100644
... ...
@@ -1,149 +0,0 @@
1
-package auth
2
-
3
-import (
4
-	"io/ioutil"
5
-	"os"
6
-	"testing"
7
-)
8
-
9
-func TestEncodeAuth(t *testing.T) {
10
-	newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
11
-	authStr := encodeAuth(newAuthConfig)
12
-	decAuthConfig := &AuthConfig{}
13
-	var err error
14
-	decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr)
15
-	if err != nil {
16
-		t.Fatal(err)
17
-	}
18
-	if newAuthConfig.Username != decAuthConfig.Username {
19
-		t.Fatal("Encode Username doesn't match decoded Username")
20
-	}
21
-	if newAuthConfig.Password != decAuthConfig.Password {
22
-		t.Fatal("Encode Password doesn't match decoded Password")
23
-	}
24
-	if authStr != "a2VuOnRlc3Q=" {
25
-		t.Fatal("AuthString encoding isn't correct.")
26
-	}
27
-}
28
-
29
-func setupTempConfigFile() (*ConfigFile, error) {
30
-	root, err := ioutil.TempDir("", "docker-test-auth")
31
-	if err != nil {
32
-		return nil, err
33
-	}
34
-	configFile := &ConfigFile{
35
-		rootPath: root,
36
-		Configs:  make(map[string]AuthConfig),
37
-	}
38
-
39
-	for _, registry := range []string{"testIndex", IndexServerAddress()} {
40
-		configFile.Configs[registry] = AuthConfig{
41
-			Username: "docker-user",
42
-			Password: "docker-pass",
43
-			Email:    "docker@docker.io",
44
-		}
45
-	}
46
-
47
-	return configFile, nil
48
-}
49
-
50
-func TestSameAuthDataPostSave(t *testing.T) {
51
-	configFile, err := setupTempConfigFile()
52
-	if err != nil {
53
-		t.Fatal(err)
54
-	}
55
-	defer os.RemoveAll(configFile.rootPath)
56
-
57
-	err = SaveConfig(configFile)
58
-	if err != nil {
59
-		t.Fatal(err)
60
-	}
61
-
62
-	authConfig := configFile.Configs["testIndex"]
63
-	if authConfig.Username != "docker-user" {
64
-		t.Fail()
65
-	}
66
-	if authConfig.Password != "docker-pass" {
67
-		t.Fail()
68
-	}
69
-	if authConfig.Email != "docker@docker.io" {
70
-		t.Fail()
71
-	}
72
-	if authConfig.Auth != "" {
73
-		t.Fail()
74
-	}
75
-}
76
-
77
-func TestResolveAuthConfigIndexServer(t *testing.T) {
78
-	configFile, err := setupTempConfigFile()
79
-	if err != nil {
80
-		t.Fatal(err)
81
-	}
82
-	defer os.RemoveAll(configFile.rootPath)
83
-
84
-	for _, registry := range []string{"", IndexServerAddress()} {
85
-		resolved := configFile.ResolveAuthConfig(registry)
86
-		if resolved != configFile.Configs[IndexServerAddress()] {
87
-			t.Fail()
88
-		}
89
-	}
90
-}
91
-
92
-func TestResolveAuthConfigFullURL(t *testing.T) {
93
-	configFile, err := setupTempConfigFile()
94
-	if err != nil {
95
-		t.Fatal(err)
96
-	}
97
-	defer os.RemoveAll(configFile.rootPath)
98
-
99
-	registryAuth := AuthConfig{
100
-		Username: "foo-user",
101
-		Password: "foo-pass",
102
-		Email:    "foo@example.com",
103
-	}
104
-	localAuth := AuthConfig{
105
-		Username: "bar-user",
106
-		Password: "bar-pass",
107
-		Email:    "bar@example.com",
108
-	}
109
-	configFile.Configs["https://registry.example.com/v1/"] = registryAuth
110
-	configFile.Configs["http://localhost:8000/v1/"] = localAuth
111
-	configFile.Configs["registry.com"] = registryAuth
112
-
113
-	validRegistries := map[string][]string{
114
-		"https://registry.example.com/v1/": {
115
-			"https://registry.example.com/v1/",
116
-			"http://registry.example.com/v1/",
117
-			"registry.example.com",
118
-			"registry.example.com/v1/",
119
-		},
120
-		"http://localhost:8000/v1/": {
121
-			"https://localhost:8000/v1/",
122
-			"http://localhost:8000/v1/",
123
-			"localhost:8000",
124
-			"localhost:8000/v1/",
125
-		},
126
-		"registry.com": {
127
-			"https://registry.com/v1/",
128
-			"http://registry.com/v1/",
129
-			"registry.com",
130
-			"registry.com/v1/",
131
-		},
132
-	}
133
-
134
-	for configKey, registries := range validRegistries {
135
-		for _, registry := range registries {
136
-			var (
137
-				configured AuthConfig
138
-				ok         bool
139
-			)
140
-			resolved := configFile.ResolveAuthConfig(registry)
141
-			if configured, ok = configFile.Configs[configKey]; !ok {
142
-				t.Fail()
143
-			}
144
-			if resolved.Email != configured.Email {
145
-				t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
146
-			}
147
-		}
148
-	}
149
-}
... ...
@@ -7,7 +7,6 @@ import (
7 7
 	"errors"
8 8
 	"fmt"
9 9
 	"github.com/dotcloud/docker/archive"
10
-	"github.com/dotcloud/docker/auth"
11 10
 	"github.com/dotcloud/docker/registry"
12 11
 	"github.com/dotcloud/docker/runconfig"
13 12
 	"github.com/dotcloud/docker/runtime"
... ...
@@ -49,8 +48,8 @@ type buildFile struct {
49 49
 	utilizeCache bool
50 50
 	rm           bool
51 51
 
52
-	authConfig *auth.AuthConfig
53
-	configFile *auth.ConfigFile
52
+	authConfig *registry.AuthConfig
53
+	configFile *registry.ConfigFile
54 54
 
55 55
 	tmpContainers map[string]struct{}
56 56
 	tmpImages     map[string]struct{}
... ...
@@ -793,7 +792,7 @@ func (b *buildFile) BuildStep(name, expression string) error {
793 793
 	return nil
794 794
 }
795 795
 
796
-func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *auth.AuthConfig, authConfigFile *auth.ConfigFile) BuildFile {
796
+func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile {
797 797
 	return &buildFile{
798 798
 		runtime:       srv.runtime,
799 799
 		srv:           srv,
... ...
@@ -4,7 +4,7 @@ import (
4 4
 	"crypto/rand"
5 5
 	"encoding/hex"
6 6
 	"fmt"
7
-	"github.com/dotcloud/docker/auth"
7
+	"github.com/dotcloud/docker/registry"
8 8
 	"os"
9 9
 	"strings"
10 10
 	"testing"
... ...
@@ -18,13 +18,13 @@ import (
18 18
 func TestLogin(t *testing.T) {
19 19
 	os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com")
20 20
 	defer os.Setenv("DOCKER_INDEX_URL", "")
21
-	authConfig := &auth.AuthConfig{
21
+	authConfig := &registry.AuthConfig{
22 22
 		Username:      "unittester",
23 23
 		Password:      "surlautrerivejetattendrai",
24 24
 		Email:         "noise+unittester@docker.com",
25 25
 		ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/",
26 26
 	}
27
-	status, err := auth.Login(authConfig, nil)
27
+	status, err := registry.Login(authConfig, nil)
28 28
 	if err != nil {
29 29
 		t.Fatal(err)
30 30
 	}
... ...
@@ -41,13 +41,13 @@ func TestCreateAccount(t *testing.T) {
41 41
 	}
42 42
 	token := hex.EncodeToString(tokenBuffer)[:12]
43 43
 	username := "ut" + token
44
-	authConfig := &auth.AuthConfig{
44
+	authConfig := &registry.AuthConfig{
45 45
 		Username:      username,
46 46
 		Password:      "test42",
47 47
 		Email:         fmt.Sprintf("docker-ut+%s@example.com", token),
48 48
 		ServerAddress: "https://indexstaging-docker.dotcloud.com/v1/",
49 49
 	}
50
-	status, err := auth.Login(authConfig, nil)
50
+	status, err := registry.Login(authConfig, nil)
51 51
 	if err != nil {
52 52
 		t.Fatal(err)
53 53
 	}
... ...
@@ -59,7 +59,7 @@ func TestCreateAccount(t *testing.T) {
59 59
 		t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status)
60 60
 	}
61 61
 
62
-	status, err = auth.Login(authConfig, nil)
62
+	status, err = registry.Login(authConfig, nil)
63 63
 	if err == nil {
64 64
 		t.Fatalf("Expected error but found nil instead")
65 65
 	}
66 66
new file mode 100644
... ...
@@ -0,0 +1,290 @@
0
+package registry
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"errors"
6
+	"fmt"
7
+	"github.com/dotcloud/docker/utils"
8
+	"io/ioutil"
9
+	"net/http"
10
+	"os"
11
+	"path"
12
+	"strings"
13
+)
14
+
15
+// Where we store the config file
16
+const CONFIGFILE = ".dockercfg"
17
+
18
+// Only used for user auth + account creation
19
+const INDEXSERVER = "https://index.docker.io/v1/"
20
+
21
+//const INDEXSERVER = "https://indexstaging-docker.dotcloud.com/v1/"
22
+
23
+var (
24
+	ErrConfigFileMissing = errors.New("The Auth config file is missing")
25
+)
26
+
27
+type AuthConfig struct {
28
+	Username      string `json:"username,omitempty"`
29
+	Password      string `json:"password,omitempty"`
30
+	Auth          string `json:"auth"`
31
+	Email         string `json:"email"`
32
+	ServerAddress string `json:"serveraddress,omitempty"`
33
+}
34
+
35
+type ConfigFile struct {
36
+	Configs  map[string]AuthConfig `json:"configs,omitempty"`
37
+	rootPath string
38
+}
39
+
40
+func IndexServerAddress() string {
41
+	return INDEXSERVER
42
+}
43
+
44
+// create a base64 encoded auth string to store in config
45
+func encodeAuth(authConfig *AuthConfig) string {
46
+	authStr := authConfig.Username + ":" + authConfig.Password
47
+	msg := []byte(authStr)
48
+	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
49
+	base64.StdEncoding.Encode(encoded, msg)
50
+	return string(encoded)
51
+}
52
+
53
+// decode the auth string
54
+func decodeAuth(authStr string) (string, string, error) {
55
+	decLen := base64.StdEncoding.DecodedLen(len(authStr))
56
+	decoded := make([]byte, decLen)
57
+	authByte := []byte(authStr)
58
+	n, err := base64.StdEncoding.Decode(decoded, authByte)
59
+	if err != nil {
60
+		return "", "", err
61
+	}
62
+	if n > decLen {
63
+		return "", "", fmt.Errorf("Something went wrong decoding auth config")
64
+	}
65
+	arr := strings.SplitN(string(decoded), ":", 2)
66
+	if len(arr) != 2 {
67
+		return "", "", fmt.Errorf("Invalid auth configuration file")
68
+	}
69
+	password := strings.Trim(arr[1], "\x00")
70
+	return arr[0], password, nil
71
+}
72
+
73
+// load up the auth config information and return values
74
+// FIXME: use the internal golang config parser
75
+func LoadConfig(rootPath string) (*ConfigFile, error) {
76
+	configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
77
+	confFile := path.Join(rootPath, CONFIGFILE)
78
+	if _, err := os.Stat(confFile); err != nil {
79
+		return &configFile, nil //missing file is not an error
80
+	}
81
+	b, err := ioutil.ReadFile(confFile)
82
+	if err != nil {
83
+		return &configFile, err
84
+	}
85
+
86
+	if err := json.Unmarshal(b, &configFile.Configs); err != nil {
87
+		arr := strings.Split(string(b), "\n")
88
+		if len(arr) < 2 {
89
+			return &configFile, fmt.Errorf("The Auth config file is empty")
90
+		}
91
+		authConfig := AuthConfig{}
92
+		origAuth := strings.Split(arr[0], " = ")
93
+		if len(origAuth) != 2 {
94
+			return &configFile, fmt.Errorf("Invalid Auth config file")
95
+		}
96
+		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
97
+		if err != nil {
98
+			return &configFile, err
99
+		}
100
+		origEmail := strings.Split(arr[1], " = ")
101
+		if len(origEmail) != 2 {
102
+			return &configFile, fmt.Errorf("Invalid Auth config file")
103
+		}
104
+		authConfig.Email = origEmail[1]
105
+		authConfig.ServerAddress = IndexServerAddress()
106
+		configFile.Configs[IndexServerAddress()] = authConfig
107
+	} else {
108
+		for k, authConfig := range configFile.Configs {
109
+			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
110
+			if err != nil {
111
+				return &configFile, err
112
+			}
113
+			authConfig.Auth = ""
114
+			configFile.Configs[k] = authConfig
115
+			authConfig.ServerAddress = k
116
+		}
117
+	}
118
+	return &configFile, nil
119
+}
120
+
121
+// save the auth config
122
+func SaveConfig(configFile *ConfigFile) error {
123
+	confFile := path.Join(configFile.rootPath, CONFIGFILE)
124
+	if len(configFile.Configs) == 0 {
125
+		os.Remove(confFile)
126
+		return nil
127
+	}
128
+
129
+	configs := make(map[string]AuthConfig, len(configFile.Configs))
130
+	for k, authConfig := range configFile.Configs {
131
+		authCopy := authConfig
132
+
133
+		authCopy.Auth = encodeAuth(&authCopy)
134
+		authCopy.Username = ""
135
+		authCopy.Password = ""
136
+		authCopy.ServerAddress = ""
137
+		configs[k] = authCopy
138
+	}
139
+
140
+	b, err := json.Marshal(configs)
141
+	if err != nil {
142
+		return err
143
+	}
144
+	err = ioutil.WriteFile(confFile, b, 0600)
145
+	if err != nil {
146
+		return err
147
+	}
148
+	return nil
149
+}
150
+
151
+// try to register/login to the registry server
152
+func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, error) {
153
+	var (
154
+		status        string
155
+		reqBody       []byte
156
+		err           error
157
+		client        = &http.Client{}
158
+		reqStatusCode = 0
159
+		serverAddress = authConfig.ServerAddress
160
+	)
161
+
162
+	if serverAddress == "" {
163
+		serverAddress = IndexServerAddress()
164
+	}
165
+
166
+	loginAgainstOfficialIndex := serverAddress == IndexServerAddress()
167
+
168
+	// to avoid sending the server address to the server it should be removed before being marshalled
169
+	authCopy := *authConfig
170
+	authCopy.ServerAddress = ""
171
+
172
+	jsonBody, err := json.Marshal(authCopy)
173
+	if err != nil {
174
+		return "", fmt.Errorf("Config Error: %s", err)
175
+	}
176
+
177
+	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
178
+	b := strings.NewReader(string(jsonBody))
179
+	req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
180
+	if err != nil {
181
+		return "", fmt.Errorf("Server Error: %s", err)
182
+	}
183
+	reqStatusCode = req1.StatusCode
184
+	defer req1.Body.Close()
185
+	reqBody, err = ioutil.ReadAll(req1.Body)
186
+	if err != nil {
187
+		return "", fmt.Errorf("Server Error: [%#v] %s", reqStatusCode, err)
188
+	}
189
+
190
+	if reqStatusCode == 201 {
191
+		if loginAgainstOfficialIndex {
192
+			status = "Account created. Please use the confirmation link we sent" +
193
+				" to your e-mail to activate it."
194
+		} else {
195
+			status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
196
+		}
197
+	} else if reqStatusCode == 400 {
198
+		if string(reqBody) == "\"Username or email already exists\"" {
199
+			req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
200
+			req.SetBasicAuth(authConfig.Username, authConfig.Password)
201
+			resp, err := client.Do(req)
202
+			if err != nil {
203
+				return "", err
204
+			}
205
+			defer resp.Body.Close()
206
+			body, err := ioutil.ReadAll(resp.Body)
207
+			if err != nil {
208
+				return "", err
209
+			}
210
+			if resp.StatusCode == 200 {
211
+				status = "Login Succeeded"
212
+			} else if resp.StatusCode == 401 {
213
+				return "", fmt.Errorf("Wrong login/password, please try again")
214
+			} else if resp.StatusCode == 403 {
215
+				if loginAgainstOfficialIndex {
216
+					return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
217
+				}
218
+				return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
219
+			} else {
220
+				return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
221
+			}
222
+		} else {
223
+			return "", fmt.Errorf("Registration: %s", reqBody)
224
+		}
225
+	} else if reqStatusCode == 401 {
226
+		// This case would happen with private registries where /v1/users is
227
+		// protected, so people can use `docker login` as an auth check.
228
+		req, err := factory.NewRequest("GET", serverAddress+"users/", nil)
229
+		req.SetBasicAuth(authConfig.Username, authConfig.Password)
230
+		resp, err := client.Do(req)
231
+		if err != nil {
232
+			return "", err
233
+		}
234
+		defer resp.Body.Close()
235
+		body, err := ioutil.ReadAll(resp.Body)
236
+		if err != nil {
237
+			return "", err
238
+		}
239
+		if resp.StatusCode == 200 {
240
+			status = "Login Succeeded"
241
+		} else if resp.StatusCode == 401 {
242
+			return "", fmt.Errorf("Wrong login/password, please try again")
243
+		} else {
244
+			return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
245
+				resp.StatusCode, resp.Header)
246
+		}
247
+	} else {
248
+		return "", fmt.Errorf("Unexpected status code [%d] : %s", reqStatusCode, reqBody)
249
+	}
250
+	return status, nil
251
+}
252
+
253
+// this method matches a auth configuration to a server address or a url
254
+func (config *ConfigFile) ResolveAuthConfig(hostname string) AuthConfig {
255
+	if hostname == IndexServerAddress() || len(hostname) == 0 {
256
+		// default to the index server
257
+		return config.Configs[IndexServerAddress()]
258
+	}
259
+
260
+	// First try the happy case
261
+	if c, found := config.Configs[hostname]; found {
262
+		return c
263
+	}
264
+
265
+	convertToHostname := func(url string) string {
266
+		stripped := url
267
+		if strings.HasPrefix(url, "http://") {
268
+			stripped = strings.Replace(url, "http://", "", 1)
269
+		} else if strings.HasPrefix(url, "https://") {
270
+			stripped = strings.Replace(url, "https://", "", 1)
271
+		}
272
+
273
+		nameParts := strings.SplitN(stripped, "/", 2)
274
+
275
+		return nameParts[0]
276
+	}
277
+
278
+	// Maybe they have a legacy config file, we will iterate the keys converting
279
+	// them to the new format and testing
280
+	normalizedHostename := convertToHostname(hostname)
281
+	for registry, config := range config.Configs {
282
+		if registryHostname := convertToHostname(registry); registryHostname == normalizedHostename {
283
+			return config
284
+		}
285
+	}
286
+
287
+	// When all else fails, return an empty auth config
288
+	return AuthConfig{}
289
+}
0 290
new file mode 100644
... ...
@@ -0,0 +1,149 @@
0
+package registry
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"testing"
6
+)
7
+
8
+func TestEncodeAuth(t *testing.T) {
9
+	newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
10
+	authStr := encodeAuth(newAuthConfig)
11
+	decAuthConfig := &AuthConfig{}
12
+	var err error
13
+	decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr)
14
+	if err != nil {
15
+		t.Fatal(err)
16
+	}
17
+	if newAuthConfig.Username != decAuthConfig.Username {
18
+		t.Fatal("Encode Username doesn't match decoded Username")
19
+	}
20
+	if newAuthConfig.Password != decAuthConfig.Password {
21
+		t.Fatal("Encode Password doesn't match decoded Password")
22
+	}
23
+	if authStr != "a2VuOnRlc3Q=" {
24
+		t.Fatal("AuthString encoding isn't correct.")
25
+	}
26
+}
27
+
28
+func setupTempConfigFile() (*ConfigFile, error) {
29
+	root, err := ioutil.TempDir("", "docker-test-auth")
30
+	if err != nil {
31
+		return nil, err
32
+	}
33
+	configFile := &ConfigFile{
34
+		rootPath: root,
35
+		Configs:  make(map[string]AuthConfig),
36
+	}
37
+
38
+	for _, registry := range []string{"testIndex", IndexServerAddress()} {
39
+		configFile.Configs[registry] = AuthConfig{
40
+			Username: "docker-user",
41
+			Password: "docker-pass",
42
+			Email:    "docker@docker.io",
43
+		}
44
+	}
45
+
46
+	return configFile, nil
47
+}
48
+
49
+func TestSameAuthDataPostSave(t *testing.T) {
50
+	configFile, err := setupTempConfigFile()
51
+	if err != nil {
52
+		t.Fatal(err)
53
+	}
54
+	defer os.RemoveAll(configFile.rootPath)
55
+
56
+	err = SaveConfig(configFile)
57
+	if err != nil {
58
+		t.Fatal(err)
59
+	}
60
+
61
+	authConfig := configFile.Configs["testIndex"]
62
+	if authConfig.Username != "docker-user" {
63
+		t.Fail()
64
+	}
65
+	if authConfig.Password != "docker-pass" {
66
+		t.Fail()
67
+	}
68
+	if authConfig.Email != "docker@docker.io" {
69
+		t.Fail()
70
+	}
71
+	if authConfig.Auth != "" {
72
+		t.Fail()
73
+	}
74
+}
75
+
76
+func TestResolveAuthConfigIndexServer(t *testing.T) {
77
+	configFile, err := setupTempConfigFile()
78
+	if err != nil {
79
+		t.Fatal(err)
80
+	}
81
+	defer os.RemoveAll(configFile.rootPath)
82
+
83
+	for _, registry := range []string{"", IndexServerAddress()} {
84
+		resolved := configFile.ResolveAuthConfig(registry)
85
+		if resolved != configFile.Configs[IndexServerAddress()] {
86
+			t.Fail()
87
+		}
88
+	}
89
+}
90
+
91
+func TestResolveAuthConfigFullURL(t *testing.T) {
92
+	configFile, err := setupTempConfigFile()
93
+	if err != nil {
94
+		t.Fatal(err)
95
+	}
96
+	defer os.RemoveAll(configFile.rootPath)
97
+
98
+	registryAuth := AuthConfig{
99
+		Username: "foo-user",
100
+		Password: "foo-pass",
101
+		Email:    "foo@example.com",
102
+	}
103
+	localAuth := AuthConfig{
104
+		Username: "bar-user",
105
+		Password: "bar-pass",
106
+		Email:    "bar@example.com",
107
+	}
108
+	configFile.Configs["https://registry.example.com/v1/"] = registryAuth
109
+	configFile.Configs["http://localhost:8000/v1/"] = localAuth
110
+	configFile.Configs["registry.com"] = registryAuth
111
+
112
+	validRegistries := map[string][]string{
113
+		"https://registry.example.com/v1/": {
114
+			"https://registry.example.com/v1/",
115
+			"http://registry.example.com/v1/",
116
+			"registry.example.com",
117
+			"registry.example.com/v1/",
118
+		},
119
+		"http://localhost:8000/v1/": {
120
+			"https://localhost:8000/v1/",
121
+			"http://localhost:8000/v1/",
122
+			"localhost:8000",
123
+			"localhost:8000/v1/",
124
+		},
125
+		"registry.com": {
126
+			"https://registry.com/v1/",
127
+			"http://registry.com/v1/",
128
+			"registry.com",
129
+			"registry.com/v1/",
130
+		},
131
+	}
132
+
133
+	for configKey, registries := range validRegistries {
134
+		for _, registry := range registries {
135
+			var (
136
+				configured AuthConfig
137
+				ok         bool
138
+			)
139
+			resolved := configFile.ResolveAuthConfig(registry)
140
+			if configured, ok = configFile.Configs[configKey]; !ok {
141
+				t.Fail()
142
+			}
143
+			if resolved.Email != configured.Email {
144
+				t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
145
+			}
146
+		}
147
+	}
148
+}
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	"encoding/json"
7 7
 	"errors"
8 8
 	"fmt"
9
-	"github.com/dotcloud/docker/auth"
10 9
 	"github.com/dotcloud/docker/utils"
11 10
 	"io"
12 11
 	"io/ioutil"
... ...
@@ -27,7 +26,7 @@ var (
27 27
 )
28 28
 
29 29
 func pingRegistryEndpoint(endpoint string) (bool, error) {
30
-	if endpoint == auth.IndexServerAddress() {
30
+	if endpoint == IndexServerAddress() {
31 31
 		// Skip the check, we now this one is valid
32 32
 		// (and we never want to fallback to http in case of error)
33 33
 		return false, nil
... ...
@@ -103,7 +102,7 @@ func ResolveRepositoryName(reposName string) (string, string, error) {
103 103
 		nameParts[0] != "localhost" {
104 104
 		// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
105 105
 		err := validateRepositoryName(reposName)
106
-		return auth.IndexServerAddress(), reposName, err
106
+		return IndexServerAddress(), reposName, err
107 107
 	}
108 108
 	if len(nameParts) < 2 {
109 109
 		// There is a dot in repos name (and no registry address)
... ...
@@ -601,7 +600,7 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat
601 601
 
602 602
 func (r *Registry) SearchRepositories(term string) (*SearchResults, error) {
603 603
 	utils.Debugf("Index server: %s", r.indexEndpoint)
604
-	u := auth.IndexServerAddress() + "search?q=" + url.QueryEscape(term)
604
+	u := IndexServerAddress() + "search?q=" + url.QueryEscape(term)
605 605
 	req, err := r.reqFactory.NewRequest("GET", u, nil)
606 606
 	if err != nil {
607 607
 		return nil, err
... ...
@@ -627,12 +626,12 @@ func (r *Registry) SearchRepositories(term string) (*SearchResults, error) {
627 627
 	return result, err
628 628
 }
629 629
 
630
-func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
630
+func (r *Registry) GetAuthConfig(withPasswd bool) *AuthConfig {
631 631
 	password := ""
632 632
 	if withPasswd {
633 633
 		password = r.authConfig.Password
634 634
 	}
635
-	return &auth.AuthConfig{
635
+	return &AuthConfig{
636 636
 		Username: r.authConfig.Username,
637 637
 		Password: password,
638 638
 		Email:    r.authConfig.Email,
... ...
@@ -668,12 +667,12 @@ type ImgData struct {
668 668
 
669 669
 type Registry struct {
670 670
 	client        *http.Client
671
-	authConfig    *auth.AuthConfig
671
+	authConfig    *AuthConfig
672 672
 	reqFactory    *utils.HTTPRequestFactory
673 673
 	indexEndpoint string
674 674
 }
675 675
 
676
-func NewRegistry(authConfig *auth.AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string) (r *Registry, err error) {
676
+func NewRegistry(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, indexEndpoint string) (r *Registry, err error) {
677 677
 	httpTransport := &http.Transport{
678 678
 		DisableKeepAlives: true,
679 679
 		Proxy:             http.ProxyFromEnvironment,
... ...
@@ -693,13 +692,13 @@ func NewRegistry(authConfig *auth.AuthConfig, factory *utils.HTTPRequestFactory,
693 693
 
694 694
 	// If we're working with a standalone private registry over HTTPS, send Basic Auth headers
695 695
 	// alongside our requests.
696
-	if indexEndpoint != auth.IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
696
+	if indexEndpoint != IndexServerAddress() && strings.HasPrefix(indexEndpoint, "https://") {
697 697
 		standalone, err := pingRegistryEndpoint(indexEndpoint)
698 698
 		if err != nil {
699 699
 			return nil, err
700 700
 		}
701 701
 		if standalone {
702
-			utils.Debugf("Endpoint %s is eligible for private registry auth. Enabling decorator.", indexEndpoint)
702
+			utils.Debugf("Endpoint %s is eligible for private registry registry. Enabling decorator.", indexEndpoint)
703 703
 			dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password)
704 704
 			factory.AddDecorator(dec)
705 705
 		}
... ...
@@ -1,7 +1,6 @@
1 1
 package registry
2 2
 
3 3
 import (
4
-	"github.com/dotcloud/docker/auth"
5 4
 	"github.com/dotcloud/docker/utils"
6 5
 	"strings"
7 6
 	"testing"
... ...
@@ -14,7 +13,7 @@ var (
14 14
 )
15 15
 
16 16
 func spawnTestRegistry(t *testing.T) *Registry {
17
-	authConfig := &auth.AuthConfig{}
17
+	authConfig := &AuthConfig{}
18 18
 	r, err := NewRegistry(authConfig, utils.NewHTTPRequestFactory(), makeURL("/v1/"))
19 19
 	if err != nil {
20 20
 		t.Fatal(err)
... ...
@@ -137,7 +136,7 @@ func TestResolveRepositoryName(t *testing.T) {
137 137
 	if err != nil {
138 138
 		t.Fatal(err)
139 139
 	}
140
-	assertEqual(t, ep, auth.IndexServerAddress(), "Expected endpoint to be index server address")
140
+	assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be index server address")
141 141
 	assertEqual(t, repo, "fooo/bar", "Expected resolved repo to be foo/bar")
142 142
 
143 143
 	u := makeURL("")[7:]
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6 6
 	"github.com/dotcloud/docker/archive"
7
-	"github.com/dotcloud/docker/auth"
8 7
 	"github.com/dotcloud/docker/daemonconfig"
9 8
 	"github.com/dotcloud/docker/dockerversion"
10 9
 	"github.com/dotcloud/docker/engine"
... ...
@@ -199,19 +198,19 @@ func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
199 199
 func (srv *Server) Auth(job *engine.Job) engine.Status {
200 200
 	var (
201 201
 		err        error
202
-		authConfig = &auth.AuthConfig{}
202
+		authConfig = &registry.AuthConfig{}
203 203
 	)
204 204
 
205 205
 	job.GetenvJson("authConfig", authConfig)
206 206
 	// TODO: this is only done here because auth and registry need to be merged into one pkg
207
-	if addr := authConfig.ServerAddress; addr != "" && addr != auth.IndexServerAddress() {
207
+	if addr := authConfig.ServerAddress; addr != "" && addr != registry.IndexServerAddress() {
208 208
 		addr, err = registry.ExpandAndVerifyRegistryUrl(addr)
209 209
 		if err != nil {
210 210
 			return job.Error(err)
211 211
 		}
212 212
 		authConfig.ServerAddress = addr
213 213
 	}
214
-	status, err := auth.Login(authConfig, srv.HTTPRequestFactory(nil))
214
+	status, err := registry.Login(authConfig, srv.HTTPRequestFactory(nil))
215 215
 	if err != nil {
216 216
 		return job.Error(err)
217 217
 	}
... ...
@@ -431,8 +430,8 @@ func (srv *Server) Build(job *engine.Job) engine.Status {
431 431
 		suppressOutput = job.GetenvBool("q")
432 432
 		noCache        = job.GetenvBool("nocache")
433 433
 		rm             = job.GetenvBool("rm")
434
-		authConfig     = &auth.AuthConfig{}
435
-		configFile     = &auth.ConfigFile{}
434
+		authConfig     = &registry.AuthConfig{}
435
+		configFile     = &registry.ConfigFile{}
436 436
 		tag            string
437 437
 		context        io.ReadCloser
438 438
 	)
... ...
@@ -611,12 +610,12 @@ func (srv *Server) ImagesSearch(job *engine.Job) engine.Status {
611 611
 	var (
612 612
 		term        = job.Args[0]
613 613
 		metaHeaders = map[string][]string{}
614
-		authConfig  = &auth.AuthConfig{}
614
+		authConfig  = &registry.AuthConfig{}
615 615
 	)
616 616
 	job.GetenvJson("authConfig", authConfig)
617 617
 	job.GetenvJson("metaHeaders", metaHeaders)
618 618
 
619
-	r, err := registry.NewRegistry(authConfig, srv.HTTPRequestFactory(metaHeaders), auth.IndexServerAddress())
619
+	r, err := registry.NewRegistry(authConfig, srv.HTTPRequestFactory(metaHeaders), registry.IndexServerAddress())
620 620
 	if err != nil {
621 621
 		return job.Error(err)
622 622
 	}
... ...
@@ -827,7 +826,7 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
827 827
 	v.Set("ExecutionDriver", srv.runtime.ExecutionDriver().Name())
828 828
 	v.SetInt("NEventsListener", len(srv.listeners))
829 829
 	v.Set("KernelVersion", kernelVersion)
830
-	v.Set("IndexServerAddress", auth.IndexServerAddress())
830
+	v.Set("IndexServerAddress", registry.IndexServerAddress())
831 831
 	v.Set("InitSha1", dockerversion.INITSHA1)
832 832
 	v.Set("InitPath", initPath)
833 833
 	if _, err := v.WriteTo(job.Stdout); err != nil {
... ...
@@ -1312,7 +1311,7 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status {
1312 1312
 		localName   = job.Args[0]
1313 1313
 		tag         string
1314 1314
 		sf          = utils.NewStreamFormatter(job.GetenvBool("json"))
1315
-		authConfig  = &auth.AuthConfig{}
1315
+		authConfig  = &registry.AuthConfig{}
1316 1316
 		metaHeaders map[string][]string
1317 1317
 	)
1318 1318
 	if len(job.Args) > 1 {
... ...
@@ -1350,7 +1349,7 @@ func (srv *Server) ImagePull(job *engine.Job) engine.Status {
1350 1350
 		return job.Error(err)
1351 1351
 	}
1352 1352
 
1353
-	if endpoint == auth.IndexServerAddress() {
1353
+	if endpoint == registry.IndexServerAddress() {
1354 1354
 		// If pull "index.docker.io/foo/bar", it's stored locally under "foo/bar"
1355 1355
 		localName = remoteName
1356 1356
 	}
... ...
@@ -1531,7 +1530,7 @@ func (srv *Server) ImagePush(job *engine.Job) engine.Status {
1531 1531
 	var (
1532 1532
 		localName   = job.Args[0]
1533 1533
 		sf          = utils.NewStreamFormatter(job.GetenvBool("json"))
1534
-		authConfig  = &auth.AuthConfig{}
1534
+		authConfig  = &registry.AuthConfig{}
1535 1535
 		metaHeaders map[string][]string
1536 1536
 	)
1537 1537