1 package auth
2
3 import (
4 "encoding/base64"
5 "fmt"
6 "net/http"
7 "net/http/httptest"
8 "net/url"
9 "testing"
10 "time"
11
12 "github.com/docker/distribution/registry/client/auth/challenge"
13 "github.com/docker/distribution/registry/client/transport"
14 "github.com/docker/distribution/testutil"
15 )
16
17
18 type fakeClock struct {
19 current time.Time
20 }
21
22
23 func (fc *fakeClock) Now() time.Time { return fc.current }
24
25 func testServer(rrm testutil.RequestResponseMap) (string, func()) {
26 h := testutil.NewHandler(rrm)
27 s := httptest.NewServer(h)
28 return s.URL, s.Close
29 }
30
31 type testAuthenticationWrapper struct {
32 headers http.Header
33 authCheck func(string) bool
34 next http.Handler
35 }
36
37 func (w *testAuthenticationWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
38 auth := r.Header.Get("Authorization")
39 if auth == "" || !w.authCheck(auth) {
40 h := rw.Header()
41 for k, values := range w.headers {
42 h[k] = values
43 }
44 rw.WriteHeader(http.StatusUnauthorized)
45 return
46 }
47 w.next.ServeHTTP(rw, r)
48 }
49
50 func testServerWithAuth(rrm testutil.RequestResponseMap, authenticate string, authCheck func(string) bool) (string, func()) {
51 h := testutil.NewHandler(rrm)
52 wrapper := &testAuthenticationWrapper{
53
54 headers: http.Header(map[string][]string{
55 "X-API-Version": {"registry/2.0"},
56 "X-Multi-API-Version": {"registry/2.0", "registry/2.1", "trust/1.0"},
57 "WWW-Authenticate": {authenticate},
58 }),
59 authCheck: authCheck,
60 next: h,
61 }
62
63 s := httptest.NewServer(wrapper)
64 return s.URL, s.Close
65 }
66
67
68
69 func ping(manager challenge.Manager, endpoint, versionHeader string) ([]APIVersion, error) {
70 resp, err := http.Get(endpoint)
71 if err != nil {
72 return nil, err
73 }
74 defer resp.Body.Close()
75
76 if err := manager.AddResponse(resp); err != nil {
77 return nil, err
78 }
79
80 return APIVersions(resp, versionHeader), err
81 }
82
83 type testCredentialStore struct {
84 username string
85 password string
86 refreshTokens map[string]string
87 }
88
89 func (tcs *testCredentialStore) Basic(*url.URL) (string, string) {
90 return tcs.username, tcs.password
91 }
92
93 func (tcs *testCredentialStore) RefreshToken(u *url.URL, service string) string {
94 return tcs.refreshTokens[service]
95 }
96
97 func (tcs *testCredentialStore) SetRefreshToken(u *url.URL, service string, token string) {
98 if tcs.refreshTokens != nil {
99 tcs.refreshTokens[service] = token
100 }
101 }
102
103 func TestEndpointAuthorizeToken(t *testing.T) {
104 service := "localhost.localdomain"
105 repo1 := "some/registry"
106 repo2 := "other/registry"
107 scope1 := fmt.Sprintf("repository:%s:pull,push", repo1)
108 scope2 := fmt.Sprintf("repository:%s:pull,push", repo2)
109 tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
110 {
111 Request: testutil.Request{
112 Method: "GET",
113 Route: fmt.Sprintf("/token?scope=%s&service=%s", url.QueryEscape(scope1), service),
114 },
115 Response: testutil.Response{
116 StatusCode: http.StatusOK,
117 Body: []byte(`{"token":"statictoken"}`),
118 },
119 },
120 {
121 Request: testutil.Request{
122 Method: "GET",
123 Route: fmt.Sprintf("/token?scope=%s&service=%s", url.QueryEscape(scope2), service),
124 },
125 Response: testutil.Response{
126 StatusCode: http.StatusOK,
127 Body: []byte(`{"token":"badtoken"}`),
128 },
129 },
130 })
131 te, tc := testServer(tokenMap)
132 defer tc()
133
134 m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
135 {
136 Request: testutil.Request{
137 Method: "GET",
138 Route: "/v2/hello",
139 },
140 Response: testutil.Response{
141 StatusCode: http.StatusAccepted,
142 },
143 },
144 })
145
146 authenicate := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service)
147 validCheck := func(a string) bool {
148 return a == "Bearer statictoken"
149 }
150 e, c := testServerWithAuth(m, authenicate, validCheck)
151 defer c()
152
153 challengeManager1 := challenge.NewSimpleManager()
154 versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
155 if err != nil {
156 t.Fatal(err)
157 }
158 if len(versions) != 1 {
159 t.Fatalf("Unexpected version count: %d, expected 1", len(versions))
160 }
161 if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check {
162 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check)
163 }
164 transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager1, NewTokenHandler(nil, nil, repo1, "pull", "push")))
165 client := &http.Client{Transport: transport1}
166
167 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
168 resp, err := client.Do(req)
169 if err != nil {
170 t.Fatalf("Error sending get request: %s", err)
171 }
172
173 if resp.StatusCode != http.StatusAccepted {
174 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
175 }
176
177 e2, c2 := testServerWithAuth(m, authenicate, validCheck)
178 defer c2()
179
180 challengeManager2 := challenge.NewSimpleManager()
181 versions, err = ping(challengeManager2, e2+"/v2/", "x-multi-api-version")
182 if err != nil {
183 t.Fatal(err)
184 }
185 if len(versions) != 3 {
186 t.Fatalf("Unexpected version count: %d, expected 3", len(versions))
187 }
188 if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check {
189 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check)
190 }
191 if check := (APIVersion{Type: "registry", Version: "2.1"}); versions[1] != check {
192 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[1], check)
193 }
194 if check := (APIVersion{Type: "trust", Version: "1.0"}); versions[2] != check {
195 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[2], check)
196 }
197 transport2 := transport.NewTransport(nil, NewAuthorizer(challengeManager2, NewTokenHandler(nil, nil, repo2, "pull", "push")))
198 client2 := &http.Client{Transport: transport2}
199
200 req, _ = http.NewRequest("GET", e2+"/v2/hello", nil)
201 resp, err = client2.Do(req)
202 if err != nil {
203 t.Fatalf("Error sending get request: %s", err)
204 }
205
206 if resp.StatusCode != http.StatusUnauthorized {
207 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusUnauthorized)
208 }
209 }
210
211 func TestEndpointAuthorizeRefreshToken(t *testing.T) {
212 service := "localhost.localdomain"
213 repo1 := "some/registry"
214 repo2 := "other/registry"
215 scope1 := fmt.Sprintf("repository:%s:pull,push", repo1)
216 scope2 := fmt.Sprintf("repository:%s:pull,push", repo2)
217 refreshToken1 := "0123456790abcdef"
218 refreshToken2 := "0123456790fedcba"
219 tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
220 {
221 Request: testutil.Request{
222 Method: "POST",
223 Route: "/token",
224 Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken1, url.QueryEscape(scope1), service)),
225 },
226 Response: testutil.Response{
227 StatusCode: http.StatusOK,
228 Body: []byte(fmt.Sprintf(`{"access_token":"statictoken","refresh_token":"%s"}`, refreshToken1)),
229 },
230 },
231 {
232
233 Request: testutil.Request{
234 Method: "POST",
235 Route: "/token",
236 Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken1, url.QueryEscape(scope2), service)),
237 },
238 Response: testutil.Response{
239 StatusCode: http.StatusOK,
240 Body: []byte(fmt.Sprintf(`{"access_token":"statictoken","refresh_token":"%s"}`, refreshToken2)),
241 },
242 },
243 {
244 Request: testutil.Request{
245 Method: "POST",
246 Route: "/token",
247 Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken2, url.QueryEscape(scope2), service)),
248 },
249 Response: testutil.Response{
250 StatusCode: http.StatusOK,
251 Body: []byte(`{"access_token":"badtoken","refresh_token":"%s"}`),
252 },
253 },
254 })
255 te, tc := testServer(tokenMap)
256 defer tc()
257
258 m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
259 {
260 Request: testutil.Request{
261 Method: "GET",
262 Route: "/v2/hello",
263 },
264 Response: testutil.Response{
265 StatusCode: http.StatusAccepted,
266 },
267 },
268 })
269
270 authenicate := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service)
271 validCheck := func(a string) bool {
272 return a == "Bearer statictoken"
273 }
274 e, c := testServerWithAuth(m, authenicate, validCheck)
275 defer c()
276
277 challengeManager1 := challenge.NewSimpleManager()
278 versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
279 if err != nil {
280 t.Fatal(err)
281 }
282 if len(versions) != 1 {
283 t.Fatalf("Unexpected version count: %d, expected 1", len(versions))
284 }
285 if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check {
286 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check)
287 }
288 creds := &testCredentialStore{
289 refreshTokens: map[string]string{
290 service: refreshToken1,
291 },
292 }
293 transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager1, NewTokenHandler(nil, creds, repo1, "pull", "push")))
294 client := &http.Client{Transport: transport1}
295
296 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
297 resp, err := client.Do(req)
298 if err != nil {
299 t.Fatalf("Error sending get request: %s", err)
300 }
301
302 if resp.StatusCode != http.StatusAccepted {
303 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
304 }
305
306
307 e2, c2 := testServerWithAuth(m, authenicate, validCheck)
308 defer c2()
309
310 challengeManager2 := challenge.NewSimpleManager()
311 versions, err = ping(challengeManager2, e2+"/v2/", "x-api-version")
312 if err != nil {
313 t.Fatal(err)
314 }
315 if len(versions) != 1 {
316 t.Fatalf("Unexpected version count: %d, expected 1", len(versions))
317 }
318 if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check {
319 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check)
320 }
321
322 transport2 := transport.NewTransport(nil, NewAuthorizer(challengeManager2, NewTokenHandler(nil, creds, repo2, "pull", "push")))
323 client2 := &http.Client{Transport: transport2}
324
325 req, _ = http.NewRequest("GET", e2+"/v2/hello", nil)
326 resp, err = client2.Do(req)
327 if err != nil {
328 t.Fatalf("Error sending get request: %s", err)
329 }
330
331 if resp.StatusCode != http.StatusAccepted {
332 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusUnauthorized)
333 }
334
335 if creds.refreshTokens[service] != refreshToken2 {
336 t.Fatalf("Refresh token not set after change")
337 }
338
339
340 e3, c3 := testServerWithAuth(m, authenicate, validCheck)
341 defer c3()
342
343 challengeManager3 := challenge.NewSimpleManager()
344 versions, err = ping(challengeManager3, e3+"/v2/", "x-api-version")
345 if err != nil {
346 t.Fatal(err)
347 }
348 if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check {
349 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check)
350 }
351
352 transport3 := transport.NewTransport(nil, NewAuthorizer(challengeManager3, NewTokenHandler(nil, creds, repo2, "pull", "push")))
353 client3 := &http.Client{Transport: transport3}
354
355 req, _ = http.NewRequest("GET", e3+"/v2/hello", nil)
356 resp, err = client3.Do(req)
357 if err != nil {
358 t.Fatalf("Error sending get request: %s", err)
359 }
360
361 if resp.StatusCode != http.StatusUnauthorized {
362 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusUnauthorized)
363 }
364 }
365
366 func TestEndpointAuthorizeV2RefreshToken(t *testing.T) {
367 service := "localhost.localdomain"
368 scope1 := "registry:catalog:search"
369 refreshToken1 := "0123456790abcdef"
370 tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
371 {
372 Request: testutil.Request{
373 Method: "POST",
374 Route: "/token",
375 Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken1, url.QueryEscape(scope1), service)),
376 },
377 Response: testutil.Response{
378 StatusCode: http.StatusOK,
379 Body: []byte(fmt.Sprintf(`{"access_token":"statictoken","refresh_token":"%s"}`, refreshToken1)),
380 },
381 },
382 })
383 te, tc := testServer(tokenMap)
384 defer tc()
385
386 m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
387 {
388 Request: testutil.Request{
389 Method: "GET",
390 Route: "/v1/search",
391 },
392 Response: testutil.Response{
393 StatusCode: http.StatusAccepted,
394 },
395 },
396 })
397
398 authenicate := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service)
399 validCheck := func(a string) bool {
400 return a == "Bearer statictoken"
401 }
402 e, c := testServerWithAuth(m, authenicate, validCheck)
403 defer c()
404
405 challengeManager1 := challenge.NewSimpleManager()
406 versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
407 if err != nil {
408 t.Fatal(err)
409 }
410 if len(versions) != 1 {
411 t.Fatalf("Unexpected version count: %d, expected 1", len(versions))
412 }
413 if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check {
414 t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check)
415 }
416 tho := TokenHandlerOptions{
417 Credentials: &testCredentialStore{
418 refreshTokens: map[string]string{
419 service: refreshToken1,
420 },
421 },
422 Scopes: []Scope{
423 RegistryScope{
424 Name: "catalog",
425 Actions: []string{"search"},
426 },
427 },
428 }
429
430 transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager1, NewTokenHandlerWithOptions(tho)))
431 client := &http.Client{Transport: transport1}
432
433 req, _ := http.NewRequest("GET", e+"/v1/search", nil)
434 resp, err := client.Do(req)
435 if err != nil {
436 t.Fatalf("Error sending get request: %s", err)
437 }
438
439 if resp.StatusCode != http.StatusAccepted {
440 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
441 }
442 }
443
444 func basicAuth(username, password string) string {
445 auth := username + ":" + password
446 return base64.StdEncoding.EncodeToString([]byte(auth))
447 }
448
449 func TestEndpointAuthorizeTokenBasic(t *testing.T) {
450 service := "localhost.localdomain"
451 repo := "some/fun/registry"
452 scope := fmt.Sprintf("repository:%s:pull,push", repo)
453 username := "tokenuser"
454 password := "superSecretPa$$word"
455
456 tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
457 {
458 Request: testutil.Request{
459 Method: "GET",
460 Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service),
461 },
462 Response: testutil.Response{
463 StatusCode: http.StatusOK,
464 Body: []byte(`{"access_token":"statictoken"}`),
465 },
466 },
467 })
468
469 authenicate1 := "Basic realm=localhost"
470 basicCheck := func(a string) bool {
471 return a == fmt.Sprintf("Basic %s", basicAuth(username, password))
472 }
473 te, tc := testServerWithAuth(tokenMap, authenicate1, basicCheck)
474 defer tc()
475
476 m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
477 {
478 Request: testutil.Request{
479 Method: "GET",
480 Route: "/v2/hello",
481 },
482 Response: testutil.Response{
483 StatusCode: http.StatusAccepted,
484 },
485 },
486 })
487
488 authenicate2 := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service)
489 bearerCheck := func(a string) bool {
490 return a == "Bearer statictoken"
491 }
492 e, c := testServerWithAuth(m, authenicate2, bearerCheck)
493 defer c()
494
495 creds := &testCredentialStore{
496 username: username,
497 password: password,
498 }
499
500 challengeManager := challenge.NewSimpleManager()
501 _, err := ping(challengeManager, e+"/v2/", "")
502 if err != nil {
503 t.Fatal(err)
504 }
505 transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager, NewTokenHandler(nil, creds, repo, "pull", "push"), NewBasicHandler(creds)))
506 client := &http.Client{Transport: transport1}
507
508 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
509 resp, err := client.Do(req)
510 if err != nil {
511 t.Fatalf("Error sending get request: %s", err)
512 }
513
514 if resp.StatusCode != http.StatusAccepted {
515 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
516 }
517 }
518
519 func TestEndpointAuthorizeTokenBasicWithExpiresIn(t *testing.T) {
520 service := "localhost.localdomain"
521 repo := "some/fun/registry"
522 scope := fmt.Sprintf("repository:%s:pull,push", repo)
523 username := "tokenuser"
524 password := "superSecretPa$$word"
525
526 tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
527 {
528 Request: testutil.Request{
529 Method: "GET",
530 Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service),
531 },
532 Response: testutil.Response{
533 StatusCode: http.StatusOK,
534 Body: []byte(`{"token":"statictoken", "expires_in": 3001}`),
535 },
536 },
537 {
538 Request: testutil.Request{
539 Method: "GET",
540 Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service),
541 },
542 Response: testutil.Response{
543 StatusCode: http.StatusOK,
544 Body: []byte(`{"access_token":"statictoken", "expires_in": 3001}`),
545 },
546 },
547 })
548
549 authenicate1 := "Basic realm=localhost"
550 tokenExchanges := 0
551 basicCheck := func(a string) bool {
552 tokenExchanges = tokenExchanges + 1
553 return a == fmt.Sprintf("Basic %s", basicAuth(username, password))
554 }
555 te, tc := testServerWithAuth(tokenMap, authenicate1, basicCheck)
556 defer tc()
557
558 m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
559 {
560 Request: testutil.Request{
561 Method: "GET",
562 Route: "/v2/hello",
563 },
564 Response: testutil.Response{
565 StatusCode: http.StatusAccepted,
566 },
567 },
568 {
569 Request: testutil.Request{
570 Method: "GET",
571 Route: "/v2/hello",
572 },
573 Response: testutil.Response{
574 StatusCode: http.StatusAccepted,
575 },
576 },
577 {
578 Request: testutil.Request{
579 Method: "GET",
580 Route: "/v2/hello",
581 },
582 Response: testutil.Response{
583 StatusCode: http.StatusAccepted,
584 },
585 },
586 {
587 Request: testutil.Request{
588 Method: "GET",
589 Route: "/v2/hello",
590 },
591 Response: testutil.Response{
592 StatusCode: http.StatusAccepted,
593 },
594 },
595 {
596 Request: testutil.Request{
597 Method: "GET",
598 Route: "/v2/hello",
599 },
600 Response: testutil.Response{
601 StatusCode: http.StatusAccepted,
602 },
603 },
604 })
605
606 authenicate2 := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service)
607 bearerCheck := func(a string) bool {
608 return a == "Bearer statictoken"
609 }
610 e, c := testServerWithAuth(m, authenicate2, bearerCheck)
611 defer c()
612
613 creds := &testCredentialStore{
614 username: username,
615 password: password,
616 }
617
618 challengeManager := challenge.NewSimpleManager()
619 _, err := ping(challengeManager, e+"/v2/", "")
620 if err != nil {
621 t.Fatal(err)
622 }
623 clock := &fakeClock{current: time.Now()}
624 options := TokenHandlerOptions{
625 Transport: nil,
626 Credentials: creds,
627 Scopes: []Scope{
628 RepositoryScope{
629 Repository: repo,
630 Actions: []string{"pull", "push"},
631 },
632 },
633 }
634 tHandler := NewTokenHandlerWithOptions(options)
635 tHandler.(*tokenHandler).clock = clock
636 transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager, tHandler, NewBasicHandler(creds)))
637 client := &http.Client{Transport: transport1}
638
639
640
641 timeIncrement := 1000 * time.Second
642 for i := 0; i < 4; i++ {
643 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
644 resp, err := client.Do(req)
645 if err != nil {
646 t.Fatalf("Error sending get request: %s", err)
647 }
648 if resp.StatusCode != http.StatusAccepted {
649 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
650 }
651 if tokenExchanges != 1 {
652 t.Fatalf("Unexpected number of token exchanges, want: 1, got %d (iteration: %d)", tokenExchanges, i)
653 }
654 clock.current = clock.current.Add(timeIncrement)
655 }
656
657
658 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
659 resp, err := client.Do(req)
660 if err != nil {
661 t.Fatalf("Error sending get request: %s", err)
662 }
663 if resp.StatusCode != http.StatusAccepted {
664 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
665 }
666 if tokenExchanges != 2 {
667 t.Fatalf("Unexpected number of token exchanges, want: 2, got %d", tokenExchanges)
668 }
669 }
670
671 func TestEndpointAuthorizeTokenBasicWithExpiresInAndIssuedAt(t *testing.T) {
672 service := "localhost.localdomain"
673 repo := "some/fun/registry"
674 scope := fmt.Sprintf("repository:%s:pull,push", repo)
675 username := "tokenuser"
676 password := "superSecretPa$$word"
677
678
679
680
681 clock := &fakeClock{current: time.Now()}
682 timeIncrement := 1000 * time.Second
683 firstIssuedAt := clock.Now()
684 clock.current = clock.current.Add(timeIncrement)
685 secondIssuedAt := clock.current.Add(2 * timeIncrement)
686 tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
687 {
688 Request: testutil.Request{
689 Method: "GET",
690 Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service),
691 },
692 Response: testutil.Response{
693 StatusCode: http.StatusOK,
694 Body: []byte(`{"token":"statictoken", "issued_at": "` + firstIssuedAt.Format(time.RFC3339Nano) + `", "expires_in": 3001}`),
695 },
696 },
697 {
698 Request: testutil.Request{
699 Method: "GET",
700 Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service),
701 },
702 Response: testutil.Response{
703 StatusCode: http.StatusOK,
704 Body: []byte(`{"access_token":"statictoken", "issued_at": "` + secondIssuedAt.Format(time.RFC3339Nano) + `", "expires_in": 3001}`),
705 },
706 },
707 })
708
709 authenicate1 := "Basic realm=localhost"
710 tokenExchanges := 0
711 basicCheck := func(a string) bool {
712 tokenExchanges = tokenExchanges + 1
713 return a == fmt.Sprintf("Basic %s", basicAuth(username, password))
714 }
715 te, tc := testServerWithAuth(tokenMap, authenicate1, basicCheck)
716 defer tc()
717
718 m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
719 {
720 Request: testutil.Request{
721 Method: "GET",
722 Route: "/v2/hello",
723 },
724 Response: testutil.Response{
725 StatusCode: http.StatusAccepted,
726 },
727 },
728 {
729 Request: testutil.Request{
730 Method: "GET",
731 Route: "/v2/hello",
732 },
733 Response: testutil.Response{
734 StatusCode: http.StatusAccepted,
735 },
736 },
737 {
738 Request: testutil.Request{
739 Method: "GET",
740 Route: "/v2/hello",
741 },
742 Response: testutil.Response{
743 StatusCode: http.StatusAccepted,
744 },
745 },
746 {
747 Request: testutil.Request{
748 Method: "GET",
749 Route: "/v2/hello",
750 },
751 Response: testutil.Response{
752 StatusCode: http.StatusAccepted,
753 },
754 },
755 })
756
757 authenicate2 := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service)
758 bearerCheck := func(a string) bool {
759 return a == "Bearer statictoken"
760 }
761 e, c := testServerWithAuth(m, authenicate2, bearerCheck)
762 defer c()
763
764 creds := &testCredentialStore{
765 username: username,
766 password: password,
767 }
768
769 challengeManager := challenge.NewSimpleManager()
770 _, err := ping(challengeManager, e+"/v2/", "")
771 if err != nil {
772 t.Fatal(err)
773 }
774
775 options := TokenHandlerOptions{
776 Transport: nil,
777 Credentials: creds,
778 Scopes: []Scope{
779 RepositoryScope{
780 Repository: repo,
781 Actions: []string{"pull", "push"},
782 },
783 },
784 }
785 tHandler := NewTokenHandlerWithOptions(options)
786 tHandler.(*tokenHandler).clock = clock
787 transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager, tHandler, NewBasicHandler(creds)))
788 client := &http.Client{Transport: transport1}
789
790
791
792
793
794 for i := 0; i < 3; i++ {
795 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
796 resp, err := client.Do(req)
797 if err != nil {
798 t.Fatalf("Error sending get request: %s", err)
799 }
800 if resp.StatusCode != http.StatusAccepted {
801 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
802 }
803 if tokenExchanges != 1 {
804 t.Fatalf("Unexpected number of token exchanges, want: 1, got %d (iteration: %d)", tokenExchanges, i)
805 }
806 clock.current = clock.current.Add(timeIncrement)
807 }
808
809
810 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
811 resp, err := client.Do(req)
812 if err != nil {
813 t.Fatalf("Error sending get request: %s", err)
814 }
815 if resp.StatusCode != http.StatusAccepted {
816 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
817 }
818 if tokenExchanges != 2 {
819 t.Fatalf("Unexpected number of token exchanges, want: 2, got %d", tokenExchanges)
820 }
821 }
822
823 func TestEndpointAuthorizeBasic(t *testing.T) {
824 m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{
825 {
826 Request: testutil.Request{
827 Method: "GET",
828 Route: "/v2/hello",
829 },
830 Response: testutil.Response{
831 StatusCode: http.StatusAccepted,
832 },
833 },
834 })
835
836 username := "user1"
837 password := "funSecretPa$$word"
838 authenicate := "Basic realm=localhost"
839 validCheck := func(a string) bool {
840 return a == fmt.Sprintf("Basic %s", basicAuth(username, password))
841 }
842 e, c := testServerWithAuth(m, authenicate, validCheck)
843 defer c()
844 creds := &testCredentialStore{
845 username: username,
846 password: password,
847 }
848
849 challengeManager := challenge.NewSimpleManager()
850 _, err := ping(challengeManager, e+"/v2/", "")
851 if err != nil {
852 t.Fatal(err)
853 }
854 transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager, NewBasicHandler(creds)))
855 client := &http.Client{Transport: transport1}
856
857 req, _ := http.NewRequest("GET", e+"/v2/hello", nil)
858 resp, err := client.Do(req)
859 if err != nil {
860 t.Fatalf("Error sending get request: %s", err)
861 }
862
863 if resp.StatusCode != http.StatusAccepted {
864 t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted)
865 }
866 }
867
View as plain text