1
15 package auth
16
17 import (
18 "context"
19 "encoding/base64"
20 "fmt"
21 "net/http"
22 "net/http/httptest"
23 "net/url"
24 "reflect"
25 "strings"
26 "sync/atomic"
27 "testing"
28 )
29
30 func TestClient_SetUserAgent(t *testing.T) {
31 wantUserAgent := "test agent"
32 var requestCount, wantRequestCount int64
33 var successCount, wantSuccessCount int64
34 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
35 atomic.AddInt64(&requestCount, 1)
36 if r.Method != http.MethodGet || r.URL.Path != "/" {
37 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
38 w.WriteHeader(http.StatusNotFound)
39 return
40 }
41 if userAgent := r.UserAgent(); userAgent != wantUserAgent {
42 t.Errorf("unexpected User-Agent: %v, want %v", userAgent, wantUserAgent)
43 return
44 }
45 atomic.AddInt64(&successCount, 1)
46 }))
47 defer ts.Close()
48
49 var client Client
50 client.SetUserAgent(wantUserAgent)
51
52 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
53 if err != nil {
54 t.Fatalf("failed to create test request: %v", err)
55 }
56 resp, err := client.Do(req)
57 if err != nil {
58 t.Fatalf("Client.Do() error = %v", err)
59 }
60 if resp.StatusCode != http.StatusOK {
61 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
62 }
63 if wantRequestCount++; requestCount != wantRequestCount {
64 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
65 }
66 if wantSuccessCount++; successCount != wantSuccessCount {
67 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
68 }
69 }
70
71 func TestClient_Do_Basic_Auth(t *testing.T) {
72 username := "test_user"
73 password := "test_password"
74 var requestCount, wantRequestCount int64
75 var successCount, wantSuccessCount int64
76 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
77 atomic.AddInt64(&requestCount, 1)
78 if r.Method != http.MethodGet || r.URL.Path != "/" {
79 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
80 w.WriteHeader(http.StatusNotFound)
81 return
82 }
83 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
84 if auth := r.Header.Get("Authorization"); auth != header {
85 w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
86 w.WriteHeader(http.StatusUnauthorized)
87 return
88 }
89 atomic.AddInt64(&successCount, 1)
90 }))
91 defer ts.Close()
92 uri, err := url.Parse(ts.URL)
93 if err != nil {
94 t.Fatalf("invalid test http server: %v", err)
95 }
96
97 client := &Client{
98 Credential: func(ctx context.Context, reg string) (Credential, error) {
99 if reg != uri.Host {
100 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
101 t.Error(err)
102 return EmptyCredential, err
103 }
104 return Credential{
105 Username: username,
106 Password: password,
107 }, nil
108 },
109 }
110
111
112 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
113 if err != nil {
114 t.Fatalf("failed to create test request: %v", err)
115 }
116 resp, err := client.Do(req)
117 if err != nil {
118 t.Fatalf("Client.Do() error = %v", err)
119 }
120 if resp.StatusCode != http.StatusOK {
121 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
122 }
123 if wantRequestCount += 2; requestCount != wantRequestCount {
124 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
125 }
126 if wantSuccessCount++; successCount != wantSuccessCount {
127 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
128 }
129
130
131 username = "test_user2"
132 password = "test_password2"
133 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
134 if err != nil {
135 t.Fatalf("failed to create test request: %v", err)
136 }
137 resp, err = client.Do(req)
138 if err != nil {
139 t.Fatalf("Client.Do() error = %v", err)
140 }
141 if resp.StatusCode != http.StatusOK {
142 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
143 }
144 if wantRequestCount += 2; requestCount != wantRequestCount {
145 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
146 }
147 if wantSuccessCount++; successCount != wantSuccessCount {
148 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
149 }
150 }
151
152 func TestClient_Do_Basic_Auth_Cached(t *testing.T) {
153 username := "test_user"
154 password := "test_password"
155 var requestCount, wantRequestCount int64
156 var successCount, wantSuccessCount int64
157 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
158 atomic.AddInt64(&requestCount, 1)
159 if r.Method != http.MethodGet || r.URL.Path != "/" {
160 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
161 w.WriteHeader(http.StatusNotFound)
162 return
163 }
164 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
165 if auth := r.Header.Get("Authorization"); auth != header {
166 w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
167 w.WriteHeader(http.StatusUnauthorized)
168 return
169 }
170 atomic.AddInt64(&successCount, 1)
171 }))
172 defer ts.Close()
173 uri, err := url.Parse(ts.URL)
174 if err != nil {
175 t.Fatalf("invalid test http server: %v", err)
176 }
177
178 client := &Client{
179 Credential: func(ctx context.Context, reg string) (Credential, error) {
180 if reg != uri.Host {
181 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
182 t.Error(err)
183 return EmptyCredential, err
184 }
185 return Credential{
186 Username: username,
187 Password: password,
188 }, nil
189 },
190 Cache: NewCache(),
191 }
192
193
194 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
195 if err != nil {
196 t.Fatalf("failed to create test request: %v", err)
197 }
198 resp, err := client.Do(req)
199 if err != nil {
200 t.Fatalf("Client.Do() error = %v", err)
201 }
202 if resp.StatusCode != http.StatusOK {
203 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
204 }
205 if wantRequestCount += 2; requestCount != wantRequestCount {
206 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
207 }
208 if wantSuccessCount++; successCount != wantSuccessCount {
209 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
210 }
211
212
213 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
214 if err != nil {
215 t.Fatalf("failed to create test request: %v", err)
216 }
217 resp, err = client.Do(req)
218 if err != nil {
219 t.Fatalf("Client.Do() error = %v", err)
220 }
221 if resp.StatusCode != http.StatusOK {
222 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
223 }
224 if wantRequestCount++; requestCount != wantRequestCount {
225 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
226 }
227 if wantSuccessCount++; successCount != wantSuccessCount {
228 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
229 }
230
231
232 username = "test_user2"
233 password = "test_password2"
234 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
235 if err != nil {
236 t.Fatalf("failed to create test request: %v", err)
237 }
238 resp, err = client.Do(req)
239 if err != nil {
240 t.Fatalf("Client.Do() error = %v", err)
241 }
242 if resp.StatusCode != http.StatusOK {
243 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
244 }
245 if wantRequestCount += 2; requestCount != wantRequestCount {
246 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
247 }
248 if wantSuccessCount++; successCount != wantSuccessCount {
249 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
250 }
251 }
252
253 func TestClient_Do_Bearer_AccessToken(t *testing.T) {
254 accessToken := "test/access/token"
255 var requestCount, wantRequestCount int64
256 var successCount, wantSuccessCount int64
257 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
258 t.Error("unexecuted attempt of authorization service")
259 w.WriteHeader(http.StatusUnauthorized)
260 }))
261 defer as.Close()
262 var service string
263 scope := "repository:test:pull,push"
264 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
265 atomic.AddInt64(&requestCount, 1)
266 if r.Method != http.MethodGet || r.URL.Path != "/" {
267 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
268 w.WriteHeader(http.StatusNotFound)
269 return
270 }
271 header := "Bearer " + accessToken
272 if auth := r.Header.Get("Authorization"); auth != header {
273 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
274 w.Header().Set("Www-Authenticate", challenge)
275 w.WriteHeader(http.StatusUnauthorized)
276 return
277 }
278 atomic.AddInt64(&successCount, 1)
279 }))
280 defer ts.Close()
281 uri, err := url.Parse(ts.URL)
282 if err != nil {
283 t.Fatalf("invalid test http server: %v", err)
284 }
285 service = uri.Host
286
287 client := &Client{
288 Credential: func(ctx context.Context, reg string) (Credential, error) {
289 if reg != uri.Host {
290 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
291 t.Error(err)
292 return EmptyCredential, err
293 }
294 return Credential{
295 AccessToken: accessToken,
296 }, nil
297 },
298 }
299
300
301 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
302 if err != nil {
303 t.Fatalf("failed to create test request: %v", err)
304 }
305 resp, err := client.Do(req)
306 if err != nil {
307 t.Fatalf("Client.Do() error = %v", err)
308 }
309 if resp.StatusCode != http.StatusOK {
310 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
311 }
312 if wantRequestCount += 2; requestCount != wantRequestCount {
313 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
314 }
315 if wantSuccessCount++; successCount != wantSuccessCount {
316 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
317 }
318
319
320 accessToken = "test/access/token/2"
321 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
322 if err != nil {
323 t.Fatalf("failed to create test request: %v", err)
324 }
325 resp, err = client.Do(req)
326 if err != nil {
327 t.Fatalf("Client.Do() error = %v", err)
328 }
329 if resp.StatusCode != http.StatusOK {
330 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
331 }
332 if wantRequestCount += 2; requestCount != wantRequestCount {
333 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
334 }
335 if wantSuccessCount++; successCount != wantSuccessCount {
336 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
337 }
338 }
339
340 func TestClient_Do_Bearer_AccessToken_Cached(t *testing.T) {
341 accessToken := "test/access/token"
342 var requestCount, wantRequestCount int64
343 var successCount, wantSuccessCount int64
344 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
345 t.Error("unexecuted attempt of authorization service")
346 w.WriteHeader(http.StatusUnauthorized)
347 }))
348 defer as.Close()
349 var service string
350 scope := "repository:test:pull,push"
351 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
352 atomic.AddInt64(&requestCount, 1)
353 if r.Method != http.MethodGet || r.URL.Path != "/" {
354 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
355 w.WriteHeader(http.StatusNotFound)
356 return
357 }
358 header := "Bearer " + accessToken
359 if auth := r.Header.Get("Authorization"); auth != header {
360 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
361 w.Header().Set("Www-Authenticate", challenge)
362 w.WriteHeader(http.StatusUnauthorized)
363 return
364 }
365 atomic.AddInt64(&successCount, 1)
366 }))
367 defer ts.Close()
368 uri, err := url.Parse(ts.URL)
369 if err != nil {
370 t.Fatalf("invalid test http server: %v", err)
371 }
372 service = uri.Host
373
374 client := &Client{
375 Credential: func(ctx context.Context, reg string) (Credential, error) {
376 if reg != uri.Host {
377 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
378 t.Error(err)
379 return EmptyCredential, err
380 }
381 return Credential{
382 AccessToken: accessToken,
383 }, nil
384 },
385 Cache: NewCache(),
386 }
387
388
389 ctx := WithScopes(context.Background(), scope)
390 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
391 if err != nil {
392 t.Fatalf("failed to create test request: %v", err)
393 }
394 resp, err := client.Do(req)
395 if err != nil {
396 t.Fatalf("Client.Do() error = %v", err)
397 }
398 if resp.StatusCode != http.StatusOK {
399 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
400 }
401 if wantRequestCount += 2; requestCount != wantRequestCount {
402 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
403 }
404 if wantSuccessCount++; successCount != wantSuccessCount {
405 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
406 }
407
408
409 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
410 if err != nil {
411 t.Fatalf("failed to create test request: %v", err)
412 }
413 resp, err = client.Do(req)
414 if err != nil {
415 t.Fatalf("Client.Do() error = %v", err)
416 }
417 if resp.StatusCode != http.StatusOK {
418 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
419 }
420 if wantRequestCount++; requestCount != wantRequestCount {
421 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
422 }
423 if wantSuccessCount++; successCount != wantSuccessCount {
424 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
425 }
426
427
428 accessToken = "test/access/token/2"
429 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
430 if err != nil {
431 t.Fatalf("failed to create test request: %v", err)
432 }
433 resp, err = client.Do(req)
434 if err != nil {
435 t.Fatalf("Client.Do() error = %v", err)
436 }
437 if resp.StatusCode != http.StatusOK {
438 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
439 }
440 if wantRequestCount += 2; requestCount != wantRequestCount {
441 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
442 }
443 if wantSuccessCount++; successCount != wantSuccessCount {
444 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
445 }
446 }
447
448 func TestClient_Do_Bearer_Auth(t *testing.T) {
449 username := "test_user"
450 password := "test_password"
451 accessToken := "test/access/token"
452 var requestCount, wantRequestCount int64
453 var successCount, wantSuccessCount int64
454 var authCount, wantAuthCount int64
455 var service string
456 scopes := []string{
457 "repository:dst:pull,push",
458 "repository:src:pull",
459 }
460 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
461 if r.Method != http.MethodGet || r.URL.Path != "/" {
462 t.Error("unexecuted attempt of authorization service")
463 w.WriteHeader(http.StatusUnauthorized)
464 return
465 }
466 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
467 if auth := r.Header.Get("Authorization"); auth != header {
468 t.Errorf("unexpected auth: got %s, want %s", auth, header)
469 w.WriteHeader(http.StatusUnauthorized)
470 return
471 }
472 if got := r.URL.Query().Get("service"); got != service {
473 t.Errorf("unexpected service: got %s, want %s", got, service)
474 w.WriteHeader(http.StatusUnauthorized)
475 return
476 }
477 if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) {
478 t.Errorf("unexpected scope: got %s, want %s", got, scopes)
479 w.WriteHeader(http.StatusUnauthorized)
480 return
481 }
482
483 atomic.AddInt64(&authCount, 1)
484 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
485 t.Errorf("failed to write %q: %v", r.URL, err)
486 }
487 }))
488 defer as.Close()
489 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
490 atomic.AddInt64(&requestCount, 1)
491 if r.Method != http.MethodGet || r.URL.Path != "/" {
492 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
493 w.WriteHeader(http.StatusNotFound)
494 return
495 }
496 header := "Bearer " + accessToken
497 if auth := r.Header.Get("Authorization"); auth != header {
498 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
499 w.Header().Set("Www-Authenticate", challenge)
500 w.WriteHeader(http.StatusUnauthorized)
501 return
502 }
503 atomic.AddInt64(&successCount, 1)
504 }))
505 defer ts.Close()
506 uri, err := url.Parse(ts.URL)
507 if err != nil {
508 t.Fatalf("invalid test http server: %v", err)
509 }
510 service = uri.Host
511
512 client := &Client{
513 Credential: func(ctx context.Context, reg string) (Credential, error) {
514 if reg != uri.Host {
515 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
516 t.Error(err)
517 return EmptyCredential, err
518 }
519 return Credential{
520 Username: username,
521 Password: password,
522 }, nil
523 },
524 }
525
526
527 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
528 if err != nil {
529 t.Fatalf("failed to create test request: %v", err)
530 }
531 resp, err := client.Do(req)
532 if err != nil {
533 t.Fatalf("Client.Do() error = %v", err)
534 }
535 if resp.StatusCode != http.StatusOK {
536 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
537 }
538 if wantRequestCount += 2; requestCount != wantRequestCount {
539 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
540 }
541 if wantSuccessCount++; successCount != wantSuccessCount {
542 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
543 }
544 if wantAuthCount++; authCount != wantAuthCount {
545 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
546 }
547
548
549 username = "test_user2"
550 password = "test_password2"
551 accessToken = "test/access/token/2"
552 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
553 if err != nil {
554 t.Fatalf("failed to create test request: %v", err)
555 }
556 resp, err = client.Do(req)
557 if err != nil {
558 t.Fatalf("Client.Do() error = %v", err)
559 }
560 if resp.StatusCode != http.StatusOK {
561 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
562 }
563 if wantRequestCount += 2; requestCount != wantRequestCount {
564 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
565 }
566 if wantSuccessCount++; successCount != wantSuccessCount {
567 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
568 }
569 if wantAuthCount++; authCount != wantAuthCount {
570 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
571 }
572 }
573
574 func TestClient_Do_Bearer_Auth_Cached(t *testing.T) {
575 username := "test_user"
576 password := "test_password"
577 accessToken := "test/access/token"
578 var requestCount, wantRequestCount int64
579 var successCount, wantSuccessCount int64
580 var authCount, wantAuthCount int64
581 var service string
582 scopes := []string{
583 "repository:dst:pull,push",
584 "repository:src:pull",
585 }
586 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
587 if r.Method != http.MethodGet || r.URL.Path != "/" {
588 t.Error("unexecuted attempt of authorization service")
589 w.WriteHeader(http.StatusUnauthorized)
590 return
591 }
592 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
593 if auth := r.Header.Get("Authorization"); auth != header {
594 t.Errorf("unexpected auth: got %s, want %s", auth, header)
595 w.WriteHeader(http.StatusUnauthorized)
596 return
597 }
598 if got := r.URL.Query().Get("service"); got != service {
599 t.Errorf("unexpected service: got %s, want %s", got, service)
600 w.WriteHeader(http.StatusUnauthorized)
601 return
602 }
603 if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) {
604 t.Errorf("unexpected scope: got %s, want %s", got, scopes)
605 w.WriteHeader(http.StatusUnauthorized)
606 return
607 }
608
609 atomic.AddInt64(&authCount, 1)
610 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
611 t.Errorf("failed to write %q: %v", r.URL, err)
612 }
613 }))
614 defer as.Close()
615 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
616 atomic.AddInt64(&requestCount, 1)
617 if r.Method != http.MethodGet || r.URL.Path != "/" {
618 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
619 w.WriteHeader(http.StatusNotFound)
620 return
621 }
622 header := "Bearer " + accessToken
623 if auth := r.Header.Get("Authorization"); auth != header {
624 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
625 w.Header().Set("Www-Authenticate", challenge)
626 w.WriteHeader(http.StatusUnauthorized)
627 return
628 }
629 atomic.AddInt64(&successCount, 1)
630 }))
631 defer ts.Close()
632 uri, err := url.Parse(ts.URL)
633 if err != nil {
634 t.Fatalf("invalid test http server: %v", err)
635 }
636 service = uri.Host
637
638 client := &Client{
639 Credential: func(ctx context.Context, reg string) (Credential, error) {
640 if reg != uri.Host {
641 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
642 t.Error(err)
643 return EmptyCredential, err
644 }
645 return Credential{
646 Username: username,
647 Password: password,
648 }, nil
649 },
650 Cache: NewCache(),
651 }
652
653
654 ctx := WithScopes(context.Background(), scopes...)
655 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
656 if err != nil {
657 t.Fatalf("failed to create test request: %v", err)
658 }
659 resp, err := client.Do(req)
660 if err != nil {
661 t.Fatalf("Client.Do() error = %v", err)
662 }
663 if resp.StatusCode != http.StatusOK {
664 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
665 }
666 if wantRequestCount += 2; requestCount != wantRequestCount {
667 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
668 }
669 if wantSuccessCount++; successCount != wantSuccessCount {
670 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
671 }
672 if wantAuthCount++; authCount != wantAuthCount {
673 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
674 }
675
676
677 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
678 if err != nil {
679 t.Fatalf("failed to create test request: %v", err)
680 }
681 resp, err = client.Do(req)
682 if err != nil {
683 t.Fatalf("Client.Do() error = %v", err)
684 }
685 if resp.StatusCode != http.StatusOK {
686 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
687 }
688 if wantRequestCount++; requestCount != wantRequestCount {
689 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
690 }
691 if wantSuccessCount++; successCount != wantSuccessCount {
692 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
693 }
694 if authCount != wantAuthCount {
695 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
696 }
697
698
699 username = "test_user2"
700 password = "test_password2"
701 accessToken = "test/access/token/2"
702 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
703 if err != nil {
704 t.Fatalf("failed to create test request: %v", err)
705 }
706 resp, err = client.Do(req)
707 if err != nil {
708 t.Fatalf("Client.Do() error = %v", err)
709 }
710 if resp.StatusCode != http.StatusOK {
711 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
712 }
713 if wantRequestCount += 2; requestCount != wantRequestCount {
714 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
715 }
716 if wantSuccessCount++; successCount != wantSuccessCount {
717 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
718 }
719 if wantAuthCount++; authCount != wantAuthCount {
720 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
721 }
722 }
723
724 func TestClient_Do_Bearer_OAuth2_Password(t *testing.T) {
725 username := "test_user"
726 password := "test_password"
727 accessToken := "test/access/token"
728 var requestCount, wantRequestCount int64
729 var successCount, wantSuccessCount int64
730 var authCount, wantAuthCount int64
731 var service string
732 scopes := []string{
733 "repository:dst:pull,push",
734 "repository:src:pull",
735 }
736 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
737 if r.Method != http.MethodPost || r.URL.Path != "/" {
738 t.Error("unexecuted attempt of authorization service")
739 w.WriteHeader(http.StatusUnauthorized)
740 return
741 }
742 if err := r.ParseForm(); err != nil {
743 t.Errorf("failed to parse form: %v", err)
744 w.WriteHeader(http.StatusUnauthorized)
745 return
746 }
747 if got := r.PostForm.Get("grant_type"); got != "password" {
748 t.Errorf("unexpected grant type: %v, want %v", got, "password")
749 w.WriteHeader(http.StatusUnauthorized)
750 return
751 }
752 if got := r.PostForm.Get("service"); got != service {
753 t.Errorf("unexpected service: %v, want %v", got, service)
754 w.WriteHeader(http.StatusUnauthorized)
755 return
756 }
757 if got := r.PostForm.Get("client_id"); got != defaultClientID {
758 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
759 w.WriteHeader(http.StatusUnauthorized)
760 return
761 }
762 scope := strings.Join(scopes, " ")
763 if got := r.PostForm.Get("scope"); got != scope {
764 t.Errorf("unexpected scope: %v, want %v", got, scope)
765 w.WriteHeader(http.StatusUnauthorized)
766 return
767 }
768 if got := r.PostForm.Get("username"); got != username {
769 t.Errorf("unexpected username: %v, want %v", got, username)
770 w.WriteHeader(http.StatusUnauthorized)
771 return
772 }
773 if got := r.PostForm.Get("password"); got != password {
774 t.Errorf("unexpected password: %v, want %v", got, password)
775 w.WriteHeader(http.StatusUnauthorized)
776 return
777 }
778
779 atomic.AddInt64(&authCount, 1)
780 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
781 t.Errorf("failed to write %q: %v", r.URL, err)
782 }
783 }))
784 defer as.Close()
785 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
786 atomic.AddInt64(&requestCount, 1)
787 if r.Method != http.MethodGet || r.URL.Path != "/" {
788 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
789 w.WriteHeader(http.StatusNotFound)
790 return
791 }
792 header := "Bearer " + accessToken
793 if auth := r.Header.Get("Authorization"); auth != header {
794 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
795 w.Header().Set("Www-Authenticate", challenge)
796 w.WriteHeader(http.StatusUnauthorized)
797 return
798 }
799 atomic.AddInt64(&successCount, 1)
800 }))
801 defer ts.Close()
802 uri, err := url.Parse(ts.URL)
803 if err != nil {
804 t.Fatalf("invalid test http server: %v", err)
805 }
806 service = uri.Host
807
808 client := &Client{
809 Credential: func(ctx context.Context, reg string) (Credential, error) {
810 if reg != uri.Host {
811 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
812 t.Error(err)
813 return EmptyCredential, err
814 }
815 return Credential{
816 Username: username,
817 Password: password,
818 }, nil
819 },
820 ForceAttemptOAuth2: true,
821 }
822
823
824 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
825 if err != nil {
826 t.Fatalf("failed to create test request: %v", err)
827 }
828 resp, err := client.Do(req)
829 if err != nil {
830 t.Fatalf("Client.Do() error = %v", err)
831 }
832 if resp.StatusCode != http.StatusOK {
833 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
834 }
835 if wantRequestCount += 2; requestCount != wantRequestCount {
836 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
837 }
838 if wantSuccessCount++; successCount != wantSuccessCount {
839 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
840 }
841 if wantAuthCount++; authCount != wantAuthCount {
842 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
843 }
844
845
846 username = "test_user2"
847 password = "test_password2"
848 accessToken = "test/access/token/2"
849 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
850 if err != nil {
851 t.Fatalf("failed to create test request: %v", err)
852 }
853 resp, err = client.Do(req)
854 if err != nil {
855 t.Fatalf("Client.Do() error = %v", err)
856 }
857 if resp.StatusCode != http.StatusOK {
858 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
859 }
860 if wantRequestCount += 2; requestCount != wantRequestCount {
861 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
862 }
863 if wantSuccessCount++; successCount != wantSuccessCount {
864 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
865 }
866 if wantAuthCount++; authCount != wantAuthCount {
867 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
868 }
869 }
870
871 func TestClient_Do_Bearer_OAuth2_Password_Cached(t *testing.T) {
872 username := "test_user"
873 password := "test_password"
874 accessToken := "test/access/token"
875 var requestCount, wantRequestCount int64
876 var successCount, wantSuccessCount int64
877 var authCount, wantAuthCount int64
878 var service string
879 scopes := []string{
880 "repository:dst:pull,push",
881 "repository:src:pull",
882 }
883 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
884 if r.Method != http.MethodPost || r.URL.Path != "/" {
885 t.Error("unexecuted attempt of authorization service")
886 w.WriteHeader(http.StatusUnauthorized)
887 return
888 }
889 if err := r.ParseForm(); err != nil {
890 t.Errorf("failed to parse form: %v", err)
891 w.WriteHeader(http.StatusUnauthorized)
892 return
893 }
894 if got := r.PostForm.Get("grant_type"); got != "password" {
895 t.Errorf("unexpected grant type: %v, want %v", got, "password")
896 w.WriteHeader(http.StatusUnauthorized)
897 return
898 }
899 if got := r.PostForm.Get("service"); got != service {
900 t.Errorf("unexpected service: %v, want %v", got, service)
901 w.WriteHeader(http.StatusUnauthorized)
902 return
903 }
904 if got := r.PostForm.Get("client_id"); got != defaultClientID {
905 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
906 w.WriteHeader(http.StatusUnauthorized)
907 return
908 }
909 scope := strings.Join(scopes, " ")
910 if got := r.PostForm.Get("scope"); got != scope {
911 t.Errorf("unexpected scope: %v, want %v", got, scope)
912 w.WriteHeader(http.StatusUnauthorized)
913 return
914 }
915 if got := r.PostForm.Get("username"); got != username {
916 t.Errorf("unexpected username: %v, want %v", got, username)
917 w.WriteHeader(http.StatusUnauthorized)
918 return
919 }
920 if got := r.PostForm.Get("password"); got != password {
921 t.Errorf("unexpected password: %v, want %v", got, password)
922 w.WriteHeader(http.StatusUnauthorized)
923 return
924 }
925
926 atomic.AddInt64(&authCount, 1)
927 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
928 t.Errorf("failed to write %q: %v", r.URL, err)
929 }
930 }))
931 defer as.Close()
932 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
933 atomic.AddInt64(&requestCount, 1)
934 if r.Method != http.MethodGet || r.URL.Path != "/" {
935 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
936 w.WriteHeader(http.StatusNotFound)
937 return
938 }
939 header := "Bearer " + accessToken
940 if auth := r.Header.Get("Authorization"); auth != header {
941 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
942 w.Header().Set("Www-Authenticate", challenge)
943 w.WriteHeader(http.StatusUnauthorized)
944 return
945 }
946 atomic.AddInt64(&successCount, 1)
947 }))
948 defer ts.Close()
949 uri, err := url.Parse(ts.URL)
950 if err != nil {
951 t.Fatalf("invalid test http server: %v", err)
952 }
953 service = uri.Host
954
955 client := &Client{
956 Credential: func(ctx context.Context, reg string) (Credential, error) {
957 if reg != uri.Host {
958 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
959 t.Error(err)
960 return EmptyCredential, err
961 }
962 return Credential{
963 Username: username,
964 Password: password,
965 }, nil
966 },
967 ForceAttemptOAuth2: true,
968 Cache: NewCache(),
969 }
970
971
972 ctx := WithScopes(context.Background(), scopes...)
973 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
974 if err != nil {
975 t.Fatalf("failed to create test request: %v", err)
976 }
977 resp, err := client.Do(req)
978 if err != nil {
979 t.Fatalf("Client.Do() error = %v", err)
980 }
981 if resp.StatusCode != http.StatusOK {
982 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
983 }
984 if wantRequestCount += 2; requestCount != wantRequestCount {
985 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
986 }
987 if wantSuccessCount++; successCount != wantSuccessCount {
988 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
989 }
990 if wantAuthCount++; authCount != wantAuthCount {
991 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
992 }
993
994
995 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
996 if err != nil {
997 t.Fatalf("failed to create test request: %v", err)
998 }
999 resp, err = client.Do(req)
1000 if err != nil {
1001 t.Fatalf("Client.Do() error = %v", err)
1002 }
1003 if resp.StatusCode != http.StatusOK {
1004 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1005 }
1006 if wantRequestCount++; requestCount != wantRequestCount {
1007 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1008 }
1009 if wantSuccessCount++; successCount != wantSuccessCount {
1010 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1011 }
1012 if authCount != wantAuthCount {
1013 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1014 }
1015
1016
1017 username = "test_user2"
1018 password = "test_password2"
1019 accessToken = "test/access/token/2"
1020 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1021 if err != nil {
1022 t.Fatalf("failed to create test request: %v", err)
1023 }
1024 resp, err = client.Do(req)
1025 if err != nil {
1026 t.Fatalf("Client.Do() error = %v", err)
1027 }
1028 if resp.StatusCode != http.StatusOK {
1029 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1030 }
1031 if wantRequestCount += 2; requestCount != wantRequestCount {
1032 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1033 }
1034 if wantSuccessCount++; successCount != wantSuccessCount {
1035 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1036 }
1037 if wantAuthCount++; authCount != wantAuthCount {
1038 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1039 }
1040 }
1041
1042 func TestClient_Do_Bearer_OAuth2_RefreshToken(t *testing.T) {
1043 refreshToken := "test/refresh/token"
1044 accessToken := "test/access/token"
1045 var requestCount, wantRequestCount int64
1046 var successCount, wantSuccessCount int64
1047 var authCount, wantAuthCount int64
1048 var service string
1049 scopes := []string{
1050 "repository:dst:pull,push",
1051 "repository:src:pull",
1052 }
1053 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1054 if r.Method != http.MethodPost || r.URL.Path != "/" {
1055 t.Error("unexecuted attempt of authorization service")
1056 w.WriteHeader(http.StatusUnauthorized)
1057 return
1058 }
1059 if err := r.ParseForm(); err != nil {
1060 t.Errorf("failed to parse form: %v", err)
1061 w.WriteHeader(http.StatusUnauthorized)
1062 return
1063 }
1064 if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
1065 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
1066 w.WriteHeader(http.StatusUnauthorized)
1067 return
1068 }
1069 if got := r.PostForm.Get("service"); got != service {
1070 t.Errorf("unexpected service: %v, want %v", got, service)
1071 w.WriteHeader(http.StatusUnauthorized)
1072 return
1073 }
1074 if got := r.PostForm.Get("client_id"); got != defaultClientID {
1075 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
1076 w.WriteHeader(http.StatusUnauthorized)
1077 return
1078 }
1079 scope := strings.Join(scopes, " ")
1080 if got := r.PostForm.Get("scope"); got != scope {
1081 t.Errorf("unexpected scope: %v, want %v", got, scope)
1082 w.WriteHeader(http.StatusUnauthorized)
1083 return
1084 }
1085 if got := r.PostForm.Get("refresh_token"); got != refreshToken {
1086 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
1087 w.WriteHeader(http.StatusUnauthorized)
1088 return
1089 }
1090
1091 atomic.AddInt64(&authCount, 1)
1092 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
1093 t.Errorf("failed to write %q: %v", r.URL, err)
1094 }
1095 }))
1096 defer as.Close()
1097 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1098 atomic.AddInt64(&requestCount, 1)
1099 if r.Method != http.MethodGet || r.URL.Path != "/" {
1100 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1101 w.WriteHeader(http.StatusNotFound)
1102 return
1103 }
1104 header := "Bearer " + accessToken
1105 if auth := r.Header.Get("Authorization"); auth != header {
1106 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
1107 w.Header().Set("Www-Authenticate", challenge)
1108 w.WriteHeader(http.StatusUnauthorized)
1109 return
1110 }
1111 atomic.AddInt64(&successCount, 1)
1112 }))
1113 defer ts.Close()
1114 uri, err := url.Parse(ts.URL)
1115 if err != nil {
1116 t.Fatalf("invalid test http server: %v", err)
1117 }
1118 service = uri.Host
1119
1120 client := &Client{
1121 Credential: func(ctx context.Context, reg string) (Credential, error) {
1122 if reg != uri.Host {
1123 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
1124 t.Error(err)
1125 return EmptyCredential, err
1126 }
1127 return Credential{
1128 RefreshToken: refreshToken,
1129 }, nil
1130 },
1131 }
1132
1133
1134 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
1135 if err != nil {
1136 t.Fatalf("failed to create test request: %v", err)
1137 }
1138 resp, err := client.Do(req)
1139 if err != nil {
1140 t.Fatalf("Client.Do() error = %v", err)
1141 }
1142 if resp.StatusCode != http.StatusOK {
1143 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1144 }
1145 if wantRequestCount += 2; requestCount != wantRequestCount {
1146 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1147 }
1148 if wantSuccessCount++; successCount != wantSuccessCount {
1149 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1150 }
1151 if wantAuthCount++; authCount != wantAuthCount {
1152 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1153 }
1154
1155
1156 refreshToken = "test/refresh/token/2"
1157 accessToken = "test/access/token/2"
1158 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
1159 if err != nil {
1160 t.Fatalf("failed to create test request: %v", err)
1161 }
1162 resp, err = client.Do(req)
1163 if err != nil {
1164 t.Fatalf("Client.Do() error = %v", err)
1165 }
1166 if resp.StatusCode != http.StatusOK {
1167 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1168 }
1169 if wantRequestCount += 2; requestCount != wantRequestCount {
1170 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1171 }
1172 if wantSuccessCount++; successCount != wantSuccessCount {
1173 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1174 }
1175 if wantAuthCount++; authCount != wantAuthCount {
1176 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1177 }
1178 }
1179
1180 func TestClient_Do_Bearer_OAuth2_RefreshToken_Cached(t *testing.T) {
1181 refreshToken := "test/refresh/token"
1182 accessToken := "test/access/token"
1183 var requestCount, wantRequestCount int64
1184 var successCount, wantSuccessCount int64
1185 var authCount, wantAuthCount int64
1186 var service string
1187 scopes := []string{
1188 "repository:dst:pull,push",
1189 "repository:src:pull",
1190 }
1191 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1192 if r.Method != http.MethodPost || r.URL.Path != "/" {
1193 t.Error("unexecuted attempt of authorization service")
1194 w.WriteHeader(http.StatusUnauthorized)
1195 return
1196 }
1197 if err := r.ParseForm(); err != nil {
1198 t.Errorf("failed to parse form: %v", err)
1199 w.WriteHeader(http.StatusUnauthorized)
1200 return
1201 }
1202 if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
1203 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
1204 w.WriteHeader(http.StatusUnauthorized)
1205 return
1206 }
1207 if got := r.PostForm.Get("service"); got != service {
1208 t.Errorf("unexpected service: %v, want %v", got, service)
1209 w.WriteHeader(http.StatusUnauthorized)
1210 return
1211 }
1212 if got := r.PostForm.Get("client_id"); got != defaultClientID {
1213 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
1214 w.WriteHeader(http.StatusUnauthorized)
1215 return
1216 }
1217 scope := strings.Join(scopes, " ")
1218 if got := r.PostForm.Get("scope"); got != scope {
1219 t.Errorf("unexpected scope: %v, want %v", got, scope)
1220 w.WriteHeader(http.StatusUnauthorized)
1221 return
1222 }
1223 if got := r.PostForm.Get("refresh_token"); got != refreshToken {
1224 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
1225 w.WriteHeader(http.StatusUnauthorized)
1226 return
1227 }
1228
1229 atomic.AddInt64(&authCount, 1)
1230 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
1231 t.Errorf("failed to write %q: %v", r.URL, err)
1232 }
1233 }))
1234 defer as.Close()
1235 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1236 atomic.AddInt64(&requestCount, 1)
1237 if r.Method != http.MethodGet || r.URL.Path != "/" {
1238 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1239 w.WriteHeader(http.StatusNotFound)
1240 return
1241 }
1242 header := "Bearer " + accessToken
1243 if auth := r.Header.Get("Authorization"); auth != header {
1244 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
1245 w.Header().Set("Www-Authenticate", challenge)
1246 w.WriteHeader(http.StatusUnauthorized)
1247 return
1248 }
1249 atomic.AddInt64(&successCount, 1)
1250 }))
1251 defer ts.Close()
1252 uri, err := url.Parse(ts.URL)
1253 if err != nil {
1254 t.Fatalf("invalid test http server: %v", err)
1255 }
1256 service = uri.Host
1257
1258 client := &Client{
1259 Credential: func(ctx context.Context, reg string) (Credential, error) {
1260 if reg != uri.Host {
1261 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
1262 t.Error(err)
1263 return EmptyCredential, err
1264 }
1265 return Credential{
1266 RefreshToken: refreshToken,
1267 }, nil
1268 },
1269 Cache: NewCache(),
1270 }
1271
1272
1273 ctx := WithScopes(context.Background(), scopes...)
1274 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1275 if err != nil {
1276 t.Fatalf("failed to create test request: %v", err)
1277 }
1278 resp, err := client.Do(req)
1279 if err != nil {
1280 t.Fatalf("Client.Do() error = %v", err)
1281 }
1282 if resp.StatusCode != http.StatusOK {
1283 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1284 }
1285 if wantRequestCount += 2; requestCount != wantRequestCount {
1286 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1287 }
1288 if wantSuccessCount++; successCount != wantSuccessCount {
1289 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1290 }
1291 if wantAuthCount++; authCount != wantAuthCount {
1292 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1293 }
1294
1295
1296 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1297 if err != nil {
1298 t.Fatalf("failed to create test request: %v", err)
1299 }
1300 resp, err = client.Do(req)
1301 if err != nil {
1302 t.Fatalf("Client.Do() error = %v", err)
1303 }
1304 if resp.StatusCode != http.StatusOK {
1305 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1306 }
1307 if wantRequestCount++; requestCount != wantRequestCount {
1308 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1309 }
1310 if wantSuccessCount++; successCount != wantSuccessCount {
1311 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1312 }
1313 if authCount != wantAuthCount {
1314 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1315 }
1316
1317
1318 refreshToken = "test/refresh/token/2"
1319 accessToken = "test/access/token/2"
1320 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1321 if err != nil {
1322 t.Fatalf("failed to create test request: %v", err)
1323 }
1324 resp, err = client.Do(req)
1325 if err != nil {
1326 t.Fatalf("Client.Do() error = %v", err)
1327 }
1328 if resp.StatusCode != http.StatusOK {
1329 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1330 }
1331 if wantRequestCount += 2; requestCount != wantRequestCount {
1332 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1333 }
1334 if wantSuccessCount++; successCount != wantSuccessCount {
1335 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1336 }
1337 if wantAuthCount++; authCount != wantAuthCount {
1338 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1339 }
1340 }
1341
1342 func TestClient_Do_Token_Expire(t *testing.T) {
1343 refreshToken := "test/refresh/token"
1344 accessToken := "test/access/token"
1345 var requestCount, wantRequestCount int64
1346 var successCount, wantSuccessCount int64
1347 var authCount, wantAuthCount int64
1348 var service string
1349 scopes := []string{
1350 "repository:dst:pull,push",
1351 "repository:src:pull",
1352 }
1353 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1354 if r.Method != http.MethodPost || r.URL.Path != "/" {
1355 t.Error("unexecuted attempt of authorization service")
1356 w.WriteHeader(http.StatusUnauthorized)
1357 return
1358 }
1359 if err := r.ParseForm(); err != nil {
1360 t.Errorf("failed to parse form: %v", err)
1361 w.WriteHeader(http.StatusUnauthorized)
1362 return
1363 }
1364 if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
1365 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
1366 w.WriteHeader(http.StatusUnauthorized)
1367 return
1368 }
1369 if got := r.PostForm.Get("service"); got != service {
1370 t.Errorf("unexpected service: %v, want %v", got, service)
1371 w.WriteHeader(http.StatusUnauthorized)
1372 return
1373 }
1374 if got := r.PostForm.Get("client_id"); got != defaultClientID {
1375 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
1376 w.WriteHeader(http.StatusUnauthorized)
1377 return
1378 }
1379 scope := strings.Join(scopes, " ")
1380 if got := r.PostForm.Get("scope"); got != scope {
1381 t.Errorf("unexpected scope: %v, want %v", got, scope)
1382 w.WriteHeader(http.StatusUnauthorized)
1383 return
1384 }
1385 if got := r.PostForm.Get("refresh_token"); got != refreshToken {
1386 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
1387 w.WriteHeader(http.StatusUnauthorized)
1388 return
1389 }
1390
1391 atomic.AddInt64(&authCount, 1)
1392 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
1393 t.Errorf("failed to write %q: %v", r.URL, err)
1394 }
1395 }))
1396 defer as.Close()
1397 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1398 atomic.AddInt64(&requestCount, 1)
1399 if r.Method != http.MethodGet || r.URL.Path != "/" {
1400 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1401 w.WriteHeader(http.StatusNotFound)
1402 return
1403 }
1404 header := "Bearer " + accessToken
1405 if auth := r.Header.Get("Authorization"); auth != header {
1406 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
1407 w.Header().Set("Www-Authenticate", challenge)
1408 w.WriteHeader(http.StatusUnauthorized)
1409 return
1410 }
1411 atomic.AddInt64(&successCount, 1)
1412 }))
1413 defer ts.Close()
1414 uri, err := url.Parse(ts.URL)
1415 if err != nil {
1416 t.Fatalf("invalid test http server: %v", err)
1417 }
1418 service = uri.Host
1419
1420 client := &Client{
1421 Credential: func(ctx context.Context, reg string) (Credential, error) {
1422 if reg != uri.Host {
1423 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
1424 t.Error(err)
1425 return EmptyCredential, err
1426 }
1427 return Credential{
1428 RefreshToken: refreshToken,
1429 }, nil
1430 },
1431 Cache: NewCache(),
1432 }
1433
1434
1435 ctx := WithScopes(context.Background(), scopes...)
1436 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1437 if err != nil {
1438 t.Fatalf("failed to create test request: %v", err)
1439 }
1440 resp, err := client.Do(req)
1441 if err != nil {
1442 t.Fatalf("Client.Do() error = %v", err)
1443 }
1444 if resp.StatusCode != http.StatusOK {
1445 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1446 }
1447 if wantRequestCount += 2; requestCount != wantRequestCount {
1448 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1449 }
1450 if wantSuccessCount++; successCount != wantSuccessCount {
1451 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1452 }
1453 if wantAuthCount++; authCount != wantAuthCount {
1454 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1455 }
1456
1457
1458 accessToken = "test/access/token/2"
1459 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1460 if err != nil {
1461 t.Fatalf("failed to create test request: %v", err)
1462 }
1463 resp, err = client.Do(req)
1464 if err != nil {
1465 t.Fatalf("Client.Do() error = %v", err)
1466 }
1467 if resp.StatusCode != http.StatusOK {
1468 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1469 }
1470 if wantRequestCount += 2; requestCount != wantRequestCount {
1471 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1472 }
1473 if wantSuccessCount++; successCount != wantSuccessCount {
1474 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1475 }
1476 if wantAuthCount++; authCount != wantAuthCount {
1477 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1478 }
1479 }
1480
1481 func TestClient_Do_Scope_Hint_Mismatch(t *testing.T) {
1482 username := "test_user"
1483 password := "test_password"
1484 accessToken := "test/access/token"
1485 var requestCount, wantRequestCount int64
1486 var successCount, wantSuccessCount int64
1487 var authCount, wantAuthCount int64
1488 var service string
1489 scopes := []string{
1490 "repository:dst:pull,push",
1491 "repository:src:pull",
1492 }
1493 scope := "repository:test:delete"
1494 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1495 if r.Method != http.MethodPost || r.URL.Path != "/" {
1496 t.Error("unexecuted attempt of authorization service")
1497 w.WriteHeader(http.StatusUnauthorized)
1498 return
1499 }
1500 if err := r.ParseForm(); err != nil {
1501 t.Errorf("failed to parse form: %v", err)
1502 w.WriteHeader(http.StatusUnauthorized)
1503 return
1504 }
1505 if got := r.PostForm.Get("grant_type"); got != "password" {
1506 t.Errorf("unexpected grant type: %v, want %v", got, "password")
1507 w.WriteHeader(http.StatusUnauthorized)
1508 return
1509 }
1510 if got := r.PostForm.Get("service"); got != service {
1511 t.Errorf("unexpected service: %v, want %v", got, service)
1512 w.WriteHeader(http.StatusUnauthorized)
1513 return
1514 }
1515 if got := r.PostForm.Get("client_id"); got != defaultClientID {
1516 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
1517 w.WriteHeader(http.StatusUnauthorized)
1518 return
1519 }
1520 scopes := CleanScopes(append([]string{scope}, scopes...))
1521 scope := strings.Join(scopes, " ")
1522 if got := r.PostForm.Get("scope"); got != scope {
1523 t.Errorf("unexpected scope: %v, want %v", got, scope)
1524 w.WriteHeader(http.StatusUnauthorized)
1525 return
1526 }
1527 if got := r.PostForm.Get("username"); got != username {
1528 t.Errorf("unexpected username: %v, want %v", got, username)
1529 w.WriteHeader(http.StatusUnauthorized)
1530 return
1531 }
1532 if got := r.PostForm.Get("password"); got != password {
1533 t.Errorf("unexpected password: %v, want %v", got, password)
1534 w.WriteHeader(http.StatusUnauthorized)
1535 return
1536 }
1537
1538 atomic.AddInt64(&authCount, 1)
1539 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
1540 t.Errorf("failed to write %q: %v", r.URL, err)
1541 }
1542 }))
1543 defer as.Close()
1544 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1545 atomic.AddInt64(&requestCount, 1)
1546 if r.Method != http.MethodGet || r.URL.Path != "/" {
1547 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1548 w.WriteHeader(http.StatusNotFound)
1549 return
1550 }
1551 header := "Bearer " + accessToken
1552 if auth := r.Header.Get("Authorization"); auth != header {
1553 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
1554 w.Header().Set("Www-Authenticate", challenge)
1555 w.WriteHeader(http.StatusUnauthorized)
1556 return
1557 }
1558 atomic.AddInt64(&successCount, 1)
1559 }))
1560 defer ts.Close()
1561 uri, err := url.Parse(ts.URL)
1562 if err != nil {
1563 t.Fatalf("invalid test http server: %v", err)
1564 }
1565 service = uri.Host
1566
1567 client := &Client{
1568 Credential: func(ctx context.Context, reg string) (Credential, error) {
1569 if reg != uri.Host {
1570 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
1571 t.Error(err)
1572 return EmptyCredential, err
1573 }
1574 return Credential{
1575 Username: username,
1576 Password: password,
1577 }, nil
1578 },
1579 ForceAttemptOAuth2: true,
1580 Cache: NewCache(),
1581 }
1582
1583
1584 ctx := WithScopes(context.Background(), scopes...)
1585 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1586 if err != nil {
1587 t.Fatalf("failed to create test request: %v", err)
1588 }
1589 resp, err := client.Do(req)
1590 if err != nil {
1591 t.Fatalf("Client.Do() error = %v", err)
1592 }
1593 if resp.StatusCode != http.StatusOK {
1594 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1595 }
1596 if wantRequestCount += 2; requestCount != wantRequestCount {
1597 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1598 }
1599 if wantSuccessCount++; successCount != wantSuccessCount {
1600 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1601 }
1602 if wantAuthCount++; authCount != wantAuthCount {
1603 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1604 }
1605
1606
1607
1608
1609
1610 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
1611 if err != nil {
1612 t.Fatalf("failed to create test request: %v", err)
1613 }
1614 resp, err = client.Do(req)
1615 if err != nil {
1616 t.Fatalf("Client.Do() error = %v", err)
1617 }
1618 if resp.StatusCode != http.StatusOK {
1619 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1620 }
1621 if wantRequestCount += 2; requestCount != wantRequestCount {
1622 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1623 }
1624 if wantSuccessCount++; successCount != wantSuccessCount {
1625 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1626 }
1627 if authCount != wantAuthCount {
1628 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1629 }
1630 }
1631
1632 func TestClient_Do_Invalid_Credential_Basic(t *testing.T) {
1633 username := "test_user"
1634 password := "test_password"
1635 var requestCount, wantRequestCount int64
1636 var successCount, wantSuccessCount int64
1637 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1638 atomic.AddInt64(&requestCount, 1)
1639 if r.Method != http.MethodGet || r.URL.Path != "/" {
1640 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1641 w.WriteHeader(http.StatusNotFound)
1642 return
1643 }
1644 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
1645 if auth := r.Header.Get("Authorization"); auth != header {
1646 w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
1647 w.WriteHeader(http.StatusUnauthorized)
1648 return
1649 }
1650 atomic.AddInt64(&successCount, 1)
1651 t.Error("authentication should fail but succeeded")
1652 }))
1653 defer ts.Close()
1654 uri, err := url.Parse(ts.URL)
1655 if err != nil {
1656 t.Fatalf("invalid test http server: %v", err)
1657 }
1658
1659 client := &Client{
1660 Credential: func(ctx context.Context, reg string) (Credential, error) {
1661 if reg != uri.Host {
1662 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
1663 t.Error(err)
1664 return EmptyCredential, err
1665 }
1666 return Credential{
1667 Username: username,
1668 Password: "bad credential",
1669 }, nil
1670 },
1671 }
1672
1673
1674 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
1675 if err != nil {
1676 t.Fatalf("failed to create test request: %v", err)
1677 }
1678 resp, err := client.Do(req)
1679 if err != nil {
1680 t.Fatalf("Client.Do() error = %v", err)
1681 }
1682 if resp.StatusCode != http.StatusUnauthorized {
1683 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusUnauthorized)
1684 }
1685 if wantRequestCount += 2; requestCount != wantRequestCount {
1686 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1687 }
1688 if successCount != wantSuccessCount {
1689 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1690 }
1691 }
1692
1693 func TestClient_Do_Invalid_Credential_Bearer(t *testing.T) {
1694 username := "test_user"
1695 password := "test_password"
1696 accessToken := "test/access/token"
1697 var requestCount, wantRequestCount int64
1698 var successCount, wantSuccessCount int64
1699 var authCount, wantAuthCount int64
1700 var service string
1701 scopes := []string{
1702 "repository:dst:pull,push",
1703 "repository:src:pull",
1704 }
1705 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1706 if r.Method != http.MethodGet || r.URL.Path != "/" {
1707 t.Error("unexecuted attempt of authorization service")
1708 w.WriteHeader(http.StatusUnauthorized)
1709 return
1710 }
1711 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
1712 if auth := r.Header.Get("Authorization"); auth != header {
1713 atomic.AddInt64(&authCount, 1)
1714 w.WriteHeader(http.StatusUnauthorized)
1715 return
1716 }
1717 t.Error("authentication should fail but succeeded")
1718 }))
1719 defer as.Close()
1720 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1721 atomic.AddInt64(&requestCount, 1)
1722 if r.Method != http.MethodGet || r.URL.Path != "/" {
1723 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1724 w.WriteHeader(http.StatusNotFound)
1725 return
1726 }
1727 header := "Bearer " + accessToken
1728 if auth := r.Header.Get("Authorization"); auth != header {
1729 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
1730 w.Header().Set("Www-Authenticate", challenge)
1731 w.WriteHeader(http.StatusUnauthorized)
1732 return
1733 }
1734 atomic.AddInt64(&successCount, 1)
1735 t.Error("authentication should fail but succeeded")
1736 }))
1737 defer ts.Close()
1738 uri, err := url.Parse(ts.URL)
1739 if err != nil {
1740 t.Fatalf("invalid test http server: %v", err)
1741 }
1742 service = uri.Host
1743
1744 client := &Client{
1745 Credential: func(ctx context.Context, reg string) (Credential, error) {
1746 if reg != uri.Host {
1747 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
1748 t.Error(err)
1749 return EmptyCredential, err
1750 }
1751 return Credential{
1752 Username: username,
1753 Password: "bad credential",
1754 }, nil
1755 },
1756 }
1757
1758
1759 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
1760 if err != nil {
1761 t.Fatalf("failed to create test request: %v", err)
1762 }
1763 _, err = client.Do(req)
1764 if err == nil {
1765 t.Fatalf("Client.Do() error = %v, wantErr %v", err, true)
1766 }
1767 if wantRequestCount++; requestCount != wantRequestCount {
1768 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1769 }
1770 if successCount != wantSuccessCount {
1771 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1772 }
1773 if wantAuthCount++; authCount != wantAuthCount {
1774 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1775 }
1776 }
1777
1778 func TestClient_Do_Anonymous_Pull(t *testing.T) {
1779 accessToken := "test/access/token"
1780 var requestCount, wantRequestCount int64
1781 var successCount, wantSuccessCount int64
1782 var authCount, wantAuthCount int64
1783 var service string
1784 scope := "repository:test:pull"
1785 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1786 if r.Method != http.MethodGet || r.URL.Path != "/" {
1787 t.Error("unexecuted attempt of authorization service")
1788 w.WriteHeader(http.StatusUnauthorized)
1789 return
1790 }
1791 if auth := r.Header.Get("Authorization"); auth != "" {
1792 t.Errorf("unexpected auth: got %s, want %s", auth, "")
1793 w.WriteHeader(http.StatusUnauthorized)
1794 return
1795 }
1796 if got := r.URL.Query().Get("service"); got != service {
1797 t.Errorf("unexpected service: got %s, want %s", got, service)
1798 w.WriteHeader(http.StatusUnauthorized)
1799 return
1800 }
1801 if got := r.URL.Query().Get("scope"); got != scope {
1802 t.Errorf("unexpected scope: got %s, want %s", got, scope)
1803 w.WriteHeader(http.StatusUnauthorized)
1804 return
1805 }
1806
1807 atomic.AddInt64(&authCount, 1)
1808 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
1809 t.Errorf("failed to write %q: %v", r.URL, err)
1810 }
1811 }))
1812 defer as.Close()
1813 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1814 atomic.AddInt64(&requestCount, 1)
1815 if r.Method != http.MethodGet || r.URL.Path != "/" {
1816 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1817 w.WriteHeader(http.StatusNotFound)
1818 return
1819 }
1820 header := "Bearer " + accessToken
1821 if auth := r.Header.Get("Authorization"); auth != header {
1822 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
1823 w.Header().Set("Www-Authenticate", challenge)
1824 w.WriteHeader(http.StatusUnauthorized)
1825 return
1826 }
1827 atomic.AddInt64(&successCount, 1)
1828 }))
1829 defer ts.Close()
1830 uri, err := url.Parse(ts.URL)
1831 if err != nil {
1832 t.Fatalf("invalid test http server: %v", err)
1833 }
1834 service = uri.Host
1835
1836
1837 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
1838 if err != nil {
1839 t.Fatalf("failed to create test request: %v", err)
1840 }
1841 resp, err := DefaultClient.Do(req)
1842 if err != nil {
1843 t.Fatalf("Client.Do() error = %v", err)
1844 }
1845 if resp.StatusCode != http.StatusOK {
1846 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1847 }
1848 if wantRequestCount += 2; requestCount != wantRequestCount {
1849 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1850 }
1851 if wantSuccessCount++; successCount != wantSuccessCount {
1852 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1853 }
1854 if wantAuthCount++; authCount != wantAuthCount {
1855 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1856 }
1857 }
1858
1859 func TestClient_Do_Scheme_Change(t *testing.T) {
1860 username := "test_user"
1861 password := "test_password"
1862 accessToken := "test/access/token"
1863 var requestCount, wantRequestCount int64
1864 var successCount, wantSuccessCount int64
1865 var authCount, wantAuthCount int64
1866 var service string
1867 scope := "repository:test:pull"
1868 challengeBearerAuth := true
1869 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1870 if r.Method != http.MethodGet || r.URL.Path != "/" {
1871 t.Error("unexecuted attempt of authorization service")
1872 w.WriteHeader(http.StatusUnauthorized)
1873 return
1874 }
1875 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
1876 if auth := r.Header.Get("Authorization"); auth != header {
1877 t.Errorf("unexpected auth: got %s, want %s", auth, header)
1878 w.WriteHeader(http.StatusUnauthorized)
1879 return
1880 }
1881 if got := r.URL.Query().Get("service"); got != service {
1882 t.Errorf("unexpected service: got %s, want %s", got, service)
1883 w.WriteHeader(http.StatusUnauthorized)
1884 return
1885 }
1886 if got := r.URL.Query().Get("scope"); got != scope {
1887 t.Errorf("unexpected scope: got %s, want %s", got, scope)
1888 w.WriteHeader(http.StatusUnauthorized)
1889 return
1890 }
1891
1892 atomic.AddInt64(&authCount, 1)
1893 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
1894 t.Errorf("failed to write %q: %v", r.URL, err)
1895 }
1896 }))
1897 defer as.Close()
1898 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1899 atomic.AddInt64(&requestCount, 1)
1900 if r.Method != http.MethodGet || r.URL.Path != "/" {
1901 t.Errorf("unexpected access: %s %s", r.Method, r.URL)
1902 w.WriteHeader(http.StatusNotFound)
1903 return
1904 }
1905 bearerHeader := "Bearer " + accessToken
1906 basicHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
1907 header := r.Header.Get("Authorization")
1908 if (challengeBearerAuth && header != bearerHeader) || (!challengeBearerAuth && header != basicHeader) {
1909 var challenge string
1910 if challengeBearerAuth {
1911 challenge = fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
1912 } else {
1913 challenge = `Basic realm="Test Server"`
1914 }
1915 w.Header().Set("Www-Authenticate", challenge)
1916 w.WriteHeader(http.StatusUnauthorized)
1917 return
1918 }
1919 atomic.AddInt64(&successCount, 1)
1920 }))
1921 defer ts.Close()
1922 uri, err := url.Parse(ts.URL)
1923 if err != nil {
1924 t.Fatalf("invalid test http server: %v", err)
1925 }
1926 service = uri.Host
1927
1928 client := &Client{
1929 Credential: func(ctx context.Context, reg string) (Credential, error) {
1930 if reg != uri.Host {
1931 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
1932 t.Error(err)
1933 return EmptyCredential, err
1934 }
1935 return Credential{
1936 Username: username,
1937 Password: password,
1938 }, nil
1939 },
1940 Cache: NewCache(),
1941 }
1942
1943
1944 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
1945 if err != nil {
1946 t.Fatalf("failed to create test request: %v", err)
1947 }
1948 resp, err := client.Do(req)
1949 if err != nil {
1950 t.Fatalf("Client.Do() error = %v", err)
1951 }
1952 if resp.StatusCode != http.StatusOK {
1953 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1954 }
1955 if wantRequestCount += 2; requestCount != wantRequestCount {
1956 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1957 }
1958 if wantSuccessCount++; successCount != wantSuccessCount {
1959 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1960 }
1961 if wantAuthCount++; authCount != wantAuthCount {
1962 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1963 }
1964
1965
1966 challengeBearerAuth = false
1967 req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
1968 if err != nil {
1969 t.Fatalf("failed to create test request: %v", err)
1970 }
1971 resp, err = client.Do(req)
1972 if err != nil {
1973 t.Fatalf("Client.Do() error = %v", err)
1974 }
1975 if resp.StatusCode != http.StatusOK {
1976 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
1977 }
1978 if wantRequestCount += 2; requestCount != wantRequestCount {
1979 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
1980 }
1981 if wantSuccessCount++; successCount != wantSuccessCount {
1982 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
1983 }
1984 if authCount != wantAuthCount {
1985 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
1986 }
1987 }
1988
View as plain text