github.com/dtroyer-salad/og2/v2@v2.0.0-20240412154159-c47231610877/registry/remote/auth/client_test.go (about) 1 /* 2 Copyright The ORAS Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package auth 17 18 import ( 19 "context" 20 "encoding/base64" 21 "errors" 22 "fmt" 23 "net/http" 24 "net/http/httptest" 25 "net/url" 26 "reflect" 27 "strings" 28 "sync/atomic" 29 "testing" 30 31 "oras.land/oras-go/v2/registry/remote/errcode" 32 ) 33 34 func TestClient_SetUserAgent(t *testing.T) { 35 wantUserAgent := "test agent" 36 var requestCount, wantRequestCount int64 37 var successCount, wantSuccessCount int64 38 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 39 atomic.AddInt64(&requestCount, 1) 40 if r.Method != http.MethodGet || r.URL.Path != "/" { 41 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 42 w.WriteHeader(http.StatusNotFound) 43 return 44 } 45 if userAgent := r.UserAgent(); userAgent != wantUserAgent { 46 t.Errorf("unexpected User-Agent: %v, want %v", userAgent, wantUserAgent) 47 return 48 } 49 atomic.AddInt64(&successCount, 1) 50 })) 51 defer ts.Close() 52 53 var client Client 54 client.SetUserAgent(wantUserAgent) 55 56 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 57 if err != nil { 58 t.Fatalf("failed to create test request: %v", err) 59 } 60 resp, err := client.Do(req) 61 if err != nil { 62 t.Fatalf("Client.Do() error = %v", err) 63 } 64 if resp.StatusCode != http.StatusOK { 65 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 66 } 67 if wantRequestCount++; requestCount != wantRequestCount { 68 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 69 } 70 if wantSuccessCount++; successCount != wantSuccessCount { 71 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 72 } 73 } 74 75 func TestClient_Do_Basic_Auth(t *testing.T) { 76 username := "test_user" 77 password := "test_password" 78 var requestCount, wantRequestCount int64 79 var successCount, wantSuccessCount int64 80 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 81 atomic.AddInt64(&requestCount, 1) 82 if r.Method != http.MethodGet || r.URL.Path != "/" { 83 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 84 w.WriteHeader(http.StatusNotFound) 85 return 86 } 87 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 88 if auth := r.Header.Get("Authorization"); auth != header { 89 w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`) 90 w.WriteHeader(http.StatusUnauthorized) 91 return 92 } 93 atomic.AddInt64(&successCount, 1) 94 })) 95 defer ts.Close() 96 uri, err := url.Parse(ts.URL) 97 if err != nil { 98 t.Fatalf("invalid test http server: %v", err) 99 } 100 101 client := &Client{ 102 Credential: func(ctx context.Context, reg string) (Credential, error) { 103 if reg != uri.Host { 104 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 105 t.Error(err) 106 return EmptyCredential, err 107 } 108 return Credential{ 109 Username: username, 110 Password: password, 111 }, nil 112 }, 113 } 114 115 // first request 116 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 117 if err != nil { 118 t.Fatalf("failed to create test request: %v", err) 119 } 120 resp, err := client.Do(req) 121 if err != nil { 122 t.Fatalf("Client.Do() error = %v", err) 123 } 124 if resp.StatusCode != http.StatusOK { 125 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 126 } 127 if wantRequestCount += 2; requestCount != wantRequestCount { 128 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 129 } 130 if wantSuccessCount++; successCount != wantSuccessCount { 131 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 132 } 133 134 // credential change 135 username = "test_user2" 136 password = "test_password2" 137 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 138 if err != nil { 139 t.Fatalf("failed to create test request: %v", err) 140 } 141 resp, err = client.Do(req) 142 if err != nil { 143 t.Fatalf("Client.Do() error = %v", err) 144 } 145 if resp.StatusCode != http.StatusOK { 146 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 147 } 148 if wantRequestCount += 2; requestCount != wantRequestCount { 149 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 150 } 151 if wantSuccessCount++; successCount != wantSuccessCount { 152 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 153 } 154 } 155 156 func TestClient_Do_Basic_Auth_Cached(t *testing.T) { 157 username := "test_user" 158 password := "test_password" 159 var requestCount, wantRequestCount int64 160 var successCount, wantSuccessCount int64 161 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 162 atomic.AddInt64(&requestCount, 1) 163 if r.Method != http.MethodGet || r.URL.Path != "/" { 164 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 165 w.WriteHeader(http.StatusNotFound) 166 return 167 } 168 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 169 if auth := r.Header.Get("Authorization"); auth != header { 170 w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`) 171 w.WriteHeader(http.StatusUnauthorized) 172 return 173 } 174 atomic.AddInt64(&successCount, 1) 175 })) 176 defer ts.Close() 177 uri, err := url.Parse(ts.URL) 178 if err != nil { 179 t.Fatalf("invalid test http server: %v", err) 180 } 181 182 client := &Client{ 183 Credential: func(ctx context.Context, reg string) (Credential, error) { 184 if reg != uri.Host { 185 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 186 t.Error(err) 187 return EmptyCredential, err 188 } 189 return Credential{ 190 Username: username, 191 Password: password, 192 }, nil 193 }, 194 Cache: NewCache(), 195 } 196 197 // first request 198 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 199 if err != nil { 200 t.Fatalf("failed to create test request: %v", err) 201 } 202 resp, err := client.Do(req) 203 if err != nil { 204 t.Fatalf("Client.Do() error = %v", err) 205 } 206 if resp.StatusCode != http.StatusOK { 207 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 208 } 209 if wantRequestCount += 2; requestCount != wantRequestCount { 210 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 211 } 212 if wantSuccessCount++; successCount != wantSuccessCount { 213 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 214 } 215 216 // repeated request 217 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 218 if err != nil { 219 t.Fatalf("failed to create test request: %v", err) 220 } 221 resp, err = client.Do(req) 222 if err != nil { 223 t.Fatalf("Client.Do() error = %v", err) 224 } 225 if resp.StatusCode != http.StatusOK { 226 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 227 } 228 if wantRequestCount++; requestCount != wantRequestCount { 229 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 230 } 231 if wantSuccessCount++; successCount != wantSuccessCount { 232 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 233 } 234 235 // credential change 236 username = "test_user2" 237 password = "test_password2" 238 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 239 if err != nil { 240 t.Fatalf("failed to create test request: %v", err) 241 } 242 resp, err = client.Do(req) 243 if err != nil { 244 t.Fatalf("Client.Do() error = %v", err) 245 } 246 if resp.StatusCode != http.StatusOK { 247 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 248 } 249 if wantRequestCount += 2; requestCount != wantRequestCount { 250 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 251 } 252 if wantSuccessCount++; successCount != wantSuccessCount { 253 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 254 } 255 } 256 257 func TestClient_Do_Bearer_AccessToken(t *testing.T) { 258 accessToken := "test/access/token" 259 var requestCount, wantRequestCount int64 260 var successCount, wantSuccessCount int64 261 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 262 t.Error("unexecuted attempt of authorization service") 263 w.WriteHeader(http.StatusUnauthorized) 264 })) 265 defer as.Close() 266 var service string 267 scope := "repository:test:pull,push" 268 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 269 atomic.AddInt64(&requestCount, 1) 270 if r.Method != http.MethodGet || r.URL.Path != "/" { 271 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 272 w.WriteHeader(http.StatusNotFound) 273 return 274 } 275 header := "Bearer " + accessToken 276 if auth := r.Header.Get("Authorization"); auth != header { 277 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope) 278 w.Header().Set("Www-Authenticate", challenge) 279 w.WriteHeader(http.StatusUnauthorized) 280 return 281 } 282 atomic.AddInt64(&successCount, 1) 283 })) 284 defer ts.Close() 285 uri, err := url.Parse(ts.URL) 286 if err != nil { 287 t.Fatalf("invalid test http server: %v", err) 288 } 289 service = uri.Host 290 291 client := &Client{ 292 Credential: func(ctx context.Context, reg string) (Credential, error) { 293 if reg != uri.Host { 294 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 295 t.Error(err) 296 return EmptyCredential, err 297 } 298 return Credential{ 299 AccessToken: accessToken, 300 }, nil 301 }, 302 } 303 304 // first request 305 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 306 if err != nil { 307 t.Fatalf("failed to create test request: %v", err) 308 } 309 resp, err := client.Do(req) 310 if err != nil { 311 t.Fatalf("Client.Do() error = %v", err) 312 } 313 if resp.StatusCode != http.StatusOK { 314 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 315 } 316 if wantRequestCount += 2; requestCount != wantRequestCount { 317 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 318 } 319 if wantSuccessCount++; successCount != wantSuccessCount { 320 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 321 } 322 323 // credential change 324 accessToken = "test/access/token/2" 325 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 326 if err != nil { 327 t.Fatalf("failed to create test request: %v", err) 328 } 329 resp, err = client.Do(req) 330 if err != nil { 331 t.Fatalf("Client.Do() error = %v", err) 332 } 333 if resp.StatusCode != http.StatusOK { 334 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 335 } 336 if wantRequestCount += 2; requestCount != wantRequestCount { 337 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 338 } 339 if wantSuccessCount++; successCount != wantSuccessCount { 340 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 341 } 342 } 343 344 func TestClient_Do_Bearer_AccessToken_Cached(t *testing.T) { 345 accessToken := "test/access/token" 346 var requestCount, wantRequestCount int64 347 var successCount, wantSuccessCount int64 348 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 349 t.Error("unexecuted attempt of authorization service") 350 w.WriteHeader(http.StatusUnauthorized) 351 })) 352 defer as.Close() 353 var service string 354 scope := "repository:test:pull,push" 355 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 356 atomic.AddInt64(&requestCount, 1) 357 if r.Method != http.MethodGet || r.URL.Path != "/" { 358 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 359 w.WriteHeader(http.StatusNotFound) 360 return 361 } 362 header := "Bearer " + accessToken 363 if auth := r.Header.Get("Authorization"); auth != header { 364 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope) 365 w.Header().Set("Www-Authenticate", challenge) 366 w.WriteHeader(http.StatusUnauthorized) 367 return 368 } 369 atomic.AddInt64(&successCount, 1) 370 })) 371 defer ts.Close() 372 uri, err := url.Parse(ts.URL) 373 if err != nil { 374 t.Fatalf("invalid test http server: %v", err) 375 } 376 service = uri.Host 377 378 client := &Client{ 379 Credential: func(ctx context.Context, reg string) (Credential, error) { 380 if reg != uri.Host { 381 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 382 t.Error(err) 383 return EmptyCredential, err 384 } 385 return Credential{ 386 AccessToken: accessToken, 387 }, nil 388 }, 389 Cache: NewCache(), 390 } 391 392 // first request 393 ctx := WithScopes(context.Background(), scope) 394 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 395 if err != nil { 396 t.Fatalf("failed to create test request: %v", err) 397 } 398 resp, err := client.Do(req) 399 if err != nil { 400 t.Fatalf("Client.Do() error = %v", err) 401 } 402 if resp.StatusCode != http.StatusOK { 403 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 404 } 405 if wantRequestCount += 2; requestCount != wantRequestCount { 406 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 407 } 408 if wantSuccessCount++; successCount != wantSuccessCount { 409 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 410 } 411 412 // repeated request 413 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 414 if err != nil { 415 t.Fatalf("failed to create test request: %v", err) 416 } 417 resp, err = client.Do(req) 418 if err != nil { 419 t.Fatalf("Client.Do() error = %v", err) 420 } 421 if resp.StatusCode != http.StatusOK { 422 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 423 } 424 if wantRequestCount++; requestCount != wantRequestCount { 425 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 426 } 427 if wantSuccessCount++; successCount != wantSuccessCount { 428 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 429 } 430 431 // credential change 432 accessToken = "test/access/token/2" 433 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 434 if err != nil { 435 t.Fatalf("failed to create test request: %v", err) 436 } 437 resp, err = client.Do(req) 438 if err != nil { 439 t.Fatalf("Client.Do() error = %v", err) 440 } 441 if resp.StatusCode != http.StatusOK { 442 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 443 } 444 if wantRequestCount += 2; requestCount != wantRequestCount { 445 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 446 } 447 if wantSuccessCount++; successCount != wantSuccessCount { 448 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 449 } 450 } 451 452 func TestClient_Do_Bearer_AccessToken_Cached_PerHost(t *testing.T) { 453 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 454 t.Error("unexecuted attempt of authorization service") 455 w.WriteHeader(http.StatusUnauthorized) 456 })) 457 defer as.Close() 458 // set up server 1 459 var requestCount1, wantRequestCount1 int64 460 var successCount1, wantSuccessCount1 int64 461 var service1 string 462 scope1 := "repository:test:pull" 463 accessToken1 := "test/access/token/1" 464 ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 465 atomic.AddInt64(&requestCount1, 1) 466 if r.Method != http.MethodGet || r.URL.Path != "/" { 467 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 468 w.WriteHeader(http.StatusNotFound) 469 return 470 } 471 header := "Bearer " + accessToken1 472 if auth := r.Header.Get("Authorization"); auth != header { 473 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service1, scope1) 474 w.Header().Set("Www-Authenticate", challenge) 475 w.WriteHeader(http.StatusUnauthorized) 476 return 477 } 478 atomic.AddInt64(&successCount1, 1) 479 })) 480 defer ts1.Close() 481 uri1, err := url.Parse(ts1.URL) 482 if err != nil { 483 t.Fatalf("invalid test http server: %v", err) 484 } 485 service1 = uri1.Host 486 client1 := &Client{ 487 Credential: StaticCredential(uri1.Host, Credential{ 488 AccessToken: accessToken1, 489 }), 490 Cache: NewCache(), 491 } 492 493 // set up server 2 494 var requestCount2, wantRequestCount2 int64 495 var successCount2, wantSuccessCount2 int64 496 var service2 string 497 scope2 := "repository:test:pull,push" 498 accessToken2 := "test/access/token/2" 499 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 500 atomic.AddInt64(&requestCount2, 1) 501 if r.Method != http.MethodGet || r.URL.Path != "/" { 502 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 503 w.WriteHeader(http.StatusNotFound) 504 return 505 } 506 header := "Bearer " + accessToken2 507 if auth := r.Header.Get("Authorization"); auth != header { 508 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service2, scope2) 509 w.Header().Set("Www-Authenticate", challenge) 510 w.WriteHeader(http.StatusUnauthorized) 511 return 512 } 513 atomic.AddInt64(&successCount2, 1) 514 })) 515 defer ts2.Close() 516 uri2, err := url.Parse(ts2.URL) 517 if err != nil { 518 t.Fatalf("invalid test http server: %v", err) 519 } 520 service2 = uri2.Host 521 client2 := &Client{ 522 Credential: StaticCredential(uri2.Host, Credential{ 523 AccessToken: accessToken2, 524 }), 525 Cache: NewCache(), 526 } 527 528 ctx := context.Background() 529 ctx = WithScopesForHost(ctx, uri1.Host, scope1) 530 ctx = WithScopesForHost(ctx, uri2.Host, scope2) 531 // first request to server 1 532 req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 533 if err != nil { 534 t.Fatalf("failed to create test request: %v", err) 535 } 536 resp1, err := client1.Do(req1) 537 if err != nil { 538 t.Fatalf("Client.Do() error = %v", err) 539 } 540 if resp1.StatusCode != http.StatusOK { 541 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 542 } 543 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 544 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 545 } 546 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 547 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 548 } 549 // first request to server 2 550 req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 551 if err != nil { 552 t.Fatalf("failed to create test request: %v", err) 553 } 554 resp2, err := client2.Do(req2) 555 if err != nil { 556 t.Fatalf("Client.Do() error = %v", err) 557 } 558 if resp2.StatusCode != http.StatusOK { 559 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 560 } 561 if wantRequestCount2 += 2; requestCount1 != wantRequestCount2 { 562 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 563 } 564 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 565 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 566 } 567 568 // repeated request to server 1 569 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 570 if err != nil { 571 t.Fatalf("failed to create test request: %v", err) 572 } 573 resp1, err = client1.Do(req1) 574 if err != nil { 575 t.Fatalf("Client.Do() error = %v", err) 576 } 577 if resp1.StatusCode != http.StatusOK { 578 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 579 } 580 if wantRequestCount1++; requestCount1 != wantRequestCount1 { 581 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 582 } 583 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 584 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 585 } 586 // repeated request to server 2 587 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 588 if err != nil { 589 t.Fatalf("failed to create test request: %v", err) 590 } 591 resp2, err = client2.Do(req2) 592 if err != nil { 593 t.Fatalf("Client.Do() error = %v", err) 594 } 595 if resp2.StatusCode != http.StatusOK { 596 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 597 } 598 if wantRequestCount2++; requestCount2 != wantRequestCount2 { 599 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 600 } 601 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 602 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 603 } 604 605 // credential change for server 1 606 accessToken1 = "test/access/token/1/new" 607 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 608 if err != nil { 609 t.Fatalf("failed to create test request: %v", err) 610 } 611 client1.Credential = StaticCredential(uri1.Host, Credential{ 612 AccessToken: accessToken1, 613 }) 614 resp1, err = client1.Do(req1) 615 if err != nil { 616 t.Fatalf("Client.Do() error = %v", err) 617 } 618 if resp1.StatusCode != http.StatusOK { 619 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 620 } 621 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 622 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 623 } 624 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 625 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 626 } 627 // credential change for server 2 628 accessToken2 = "test/access/token/2/new" 629 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 630 if err != nil { 631 t.Fatalf("failed to create test request: %v", err) 632 } 633 client2.Credential = StaticCredential(uri2.Host, Credential{ 634 AccessToken: accessToken2, 635 }) 636 resp2, err = client2.Do(req2) 637 if err != nil { 638 t.Fatalf("Client.Do() error = %v", err) 639 } 640 if resp2.StatusCode != http.StatusOK { 641 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 642 } 643 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 644 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 645 } 646 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 647 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 648 } 649 } 650 651 func TestClient_Do_Bearer_Auth(t *testing.T) { 652 username := "test_user" 653 password := "test_password" 654 accessToken := "test/access/token" 655 var requestCount, wantRequestCount int64 656 var successCount, wantSuccessCount int64 657 var authCount, wantAuthCount int64 658 var service string 659 scopes := []string{ 660 "repository:dst:pull,push", 661 "repository:src:pull", 662 } 663 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 664 if r.Method != http.MethodGet || r.URL.Path != "/" { 665 t.Error("unexecuted attempt of authorization service") 666 w.WriteHeader(http.StatusUnauthorized) 667 return 668 } 669 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 670 if auth := r.Header.Get("Authorization"); auth != header { 671 t.Errorf("unexpected auth: got %s, want %s", auth, header) 672 w.WriteHeader(http.StatusUnauthorized) 673 return 674 } 675 if got := r.URL.Query().Get("service"); got != service { 676 t.Errorf("unexpected service: got %s, want %s", got, service) 677 w.WriteHeader(http.StatusUnauthorized) 678 return 679 } 680 if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) { 681 t.Errorf("unexpected scope: got %s, want %s", got, scopes) 682 w.WriteHeader(http.StatusUnauthorized) 683 return 684 } 685 686 atomic.AddInt64(&authCount, 1) 687 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 688 t.Errorf("failed to write %q: %v", r.URL, err) 689 } 690 })) 691 defer as.Close() 692 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 693 atomic.AddInt64(&requestCount, 1) 694 if r.Method != http.MethodGet || r.URL.Path != "/" { 695 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 696 w.WriteHeader(http.StatusNotFound) 697 return 698 } 699 header := "Bearer " + accessToken 700 if auth := r.Header.Get("Authorization"); auth != header { 701 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 702 w.Header().Set("Www-Authenticate", challenge) 703 w.WriteHeader(http.StatusUnauthorized) 704 return 705 } 706 atomic.AddInt64(&successCount, 1) 707 })) 708 defer ts.Close() 709 uri, err := url.Parse(ts.URL) 710 if err != nil { 711 t.Fatalf("invalid test http server: %v", err) 712 } 713 service = uri.Host 714 715 client := &Client{ 716 Credential: func(ctx context.Context, reg string) (Credential, error) { 717 if reg != uri.Host { 718 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 719 t.Error(err) 720 return EmptyCredential, err 721 } 722 return Credential{ 723 Username: username, 724 Password: password, 725 }, nil 726 }, 727 } 728 729 // first request 730 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 731 if err != nil { 732 t.Fatalf("failed to create test request: %v", err) 733 } 734 resp, err := client.Do(req) 735 if err != nil { 736 t.Fatalf("Client.Do() error = %v", err) 737 } 738 if resp.StatusCode != http.StatusOK { 739 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 740 } 741 if wantRequestCount += 2; requestCount != wantRequestCount { 742 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 743 } 744 if wantSuccessCount++; successCount != wantSuccessCount { 745 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 746 } 747 if wantAuthCount++; authCount != wantAuthCount { 748 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 749 } 750 751 // credential change 752 username = "test_user2" 753 password = "test_password2" 754 accessToken = "test/access/token/2" 755 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 756 if err != nil { 757 t.Fatalf("failed to create test request: %v", err) 758 } 759 resp, err = client.Do(req) 760 if err != nil { 761 t.Fatalf("Client.Do() error = %v", err) 762 } 763 if resp.StatusCode != http.StatusOK { 764 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 765 } 766 if wantRequestCount += 2; requestCount != wantRequestCount { 767 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 768 } 769 if wantSuccessCount++; successCount != wantSuccessCount { 770 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 771 } 772 if wantAuthCount++; authCount != wantAuthCount { 773 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 774 } 775 } 776 777 func TestClient_Do_Bearer_Auth_Cached(t *testing.T) { 778 username := "test_user" 779 password := "test_password" 780 accessToken := "test/access/token" 781 var requestCount, wantRequestCount int64 782 var successCount, wantSuccessCount int64 783 var authCount, wantAuthCount int64 784 var service string 785 scopes := []string{ 786 "repository:dst:pull,push", 787 "repository:src:pull", 788 } 789 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 790 if r.Method != http.MethodGet || r.URL.Path != "/" { 791 t.Error("unexecuted attempt of authorization service") 792 w.WriteHeader(http.StatusUnauthorized) 793 return 794 } 795 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 796 if auth := r.Header.Get("Authorization"); auth != header { 797 t.Errorf("unexpected auth: got %s, want %s", auth, header) 798 w.WriteHeader(http.StatusUnauthorized) 799 return 800 } 801 if got := r.URL.Query().Get("service"); got != service { 802 t.Errorf("unexpected service: got %s, want %s", got, service) 803 w.WriteHeader(http.StatusUnauthorized) 804 return 805 } 806 if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) { 807 t.Errorf("unexpected scope: got %s, want %s", got, scopes) 808 w.WriteHeader(http.StatusUnauthorized) 809 return 810 } 811 812 atomic.AddInt64(&authCount, 1) 813 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 814 t.Errorf("failed to write %q: %v", r.URL, err) 815 } 816 })) 817 defer as.Close() 818 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 819 atomic.AddInt64(&requestCount, 1) 820 if r.Method != http.MethodGet || r.URL.Path != "/" { 821 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 822 w.WriteHeader(http.StatusNotFound) 823 return 824 } 825 header := "Bearer " + accessToken 826 if auth := r.Header.Get("Authorization"); auth != header { 827 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 828 w.Header().Set("Www-Authenticate", challenge) 829 w.WriteHeader(http.StatusUnauthorized) 830 return 831 } 832 atomic.AddInt64(&successCount, 1) 833 })) 834 defer ts.Close() 835 uri, err := url.Parse(ts.URL) 836 if err != nil { 837 t.Fatalf("invalid test http server: %v", err) 838 } 839 service = uri.Host 840 841 client := &Client{ 842 Credential: func(ctx context.Context, reg string) (Credential, error) { 843 if reg != uri.Host { 844 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 845 t.Error(err) 846 return EmptyCredential, err 847 } 848 return Credential{ 849 Username: username, 850 Password: password, 851 }, nil 852 }, 853 Cache: NewCache(), 854 } 855 856 // first request 857 ctx := WithScopes(context.Background(), scopes...) 858 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 859 if err != nil { 860 t.Fatalf("failed to create test request: %v", err) 861 } 862 resp, err := client.Do(req) 863 if err != nil { 864 t.Fatalf("Client.Do() error = %v", err) 865 } 866 if resp.StatusCode != http.StatusOK { 867 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 868 } 869 if wantRequestCount += 2; requestCount != wantRequestCount { 870 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 871 } 872 if wantSuccessCount++; successCount != wantSuccessCount { 873 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 874 } 875 if wantAuthCount++; authCount != wantAuthCount { 876 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 877 } 878 879 // repeated request 880 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 881 if err != nil { 882 t.Fatalf("failed to create test request: %v", err) 883 } 884 resp, err = client.Do(req) 885 if err != nil { 886 t.Fatalf("Client.Do() error = %v", err) 887 } 888 if resp.StatusCode != http.StatusOK { 889 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 890 } 891 if wantRequestCount++; requestCount != wantRequestCount { 892 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 893 } 894 if wantSuccessCount++; successCount != wantSuccessCount { 895 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 896 } 897 if authCount != wantAuthCount { 898 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 899 } 900 901 // credential change 902 username = "test_user2" 903 password = "test_password2" 904 accessToken = "test/access/token/2" 905 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 906 if err != nil { 907 t.Fatalf("failed to create test request: %v", err) 908 } 909 resp, err = client.Do(req) 910 if err != nil { 911 t.Fatalf("Client.Do() error = %v", err) 912 } 913 if resp.StatusCode != http.StatusOK { 914 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 915 } 916 if wantRequestCount += 2; requestCount != wantRequestCount { 917 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 918 } 919 if wantSuccessCount++; successCount != wantSuccessCount { 920 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 921 } 922 if wantAuthCount++; authCount != wantAuthCount { 923 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 924 } 925 } 926 927 func TestClient_Do_Bearer_Auth_Cached_PerHost(t *testing.T) { 928 // set up server 1 929 username1 := "test_user1" 930 password1 := "test_password1" 931 accessToken1 := "test/access/token/1" 932 var requestCount1, wantRequestCount1 int64 933 var successCount1, wantSuccessCount1 int64 934 var authCount1, wantAuthCount1 int64 935 var service1 string 936 scopes1 := []string{ 937 "repository:src:pull", 938 } 939 as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 940 if r.Method != http.MethodGet || r.URL.Path != "/" { 941 t.Error("unexecuted attempt of authorization service") 942 w.WriteHeader(http.StatusUnauthorized) 943 return 944 } 945 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username1+":"+password1)) 946 if auth := r.Header.Get("Authorization"); auth != header { 947 t.Errorf("unexpected auth: got %s, want %s", auth, header) 948 w.WriteHeader(http.StatusUnauthorized) 949 return 950 } 951 if got := r.URL.Query().Get("service"); got != service1 { 952 t.Errorf("unexpected service: got %s, want %s", got, service1) 953 w.WriteHeader(http.StatusUnauthorized) 954 return 955 } 956 if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes1) { 957 t.Errorf("unexpected scope: got %s, want %s", got, scopes1) 958 w.WriteHeader(http.StatusUnauthorized) 959 return 960 } 961 962 atomic.AddInt64(&authCount1, 1) 963 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil { 964 t.Errorf("failed to write %q: %v", r.URL, err) 965 } 966 })) 967 defer as1.Close() 968 ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 969 atomic.AddInt64(&requestCount1, 1) 970 if r.Method != http.MethodGet || r.URL.Path != "/" { 971 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 972 w.WriteHeader(http.StatusNotFound) 973 return 974 } 975 header := "Bearer " + accessToken1 976 if auth := r.Header.Get("Authorization"); auth != header { 977 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " ")) 978 w.Header().Set("Www-Authenticate", challenge) 979 w.WriteHeader(http.StatusUnauthorized) 980 return 981 } 982 atomic.AddInt64(&successCount1, 1) 983 })) 984 defer ts1.Close() 985 uri1, err := url.Parse(ts1.URL) 986 if err != nil { 987 t.Fatalf("invalid test http server: %v", err) 988 } 989 service1 = uri1.Host 990 client1 := &Client{ 991 Credential: StaticCredential(uri1.Host, Credential{ 992 Username: username1, 993 Password: password1, 994 }), 995 Cache: NewCache(), 996 } 997 998 // set up server 2 999 username2 := "test_user2" 1000 password2 := "test_password2" 1001 accessToken2 := "test/access/token/1" 1002 var requestCount2, wantRequestCount2 int64 1003 var successCount2, wantSuccessCount2 int64 1004 var authCount2, wantAuthCount2 int64 1005 var service2 string 1006 scopes2 := []string{ 1007 "repository:dst:pull,push", 1008 } 1009 as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1010 if r.Method != http.MethodGet || r.URL.Path != "/" { 1011 t.Error("unexecuted attempt of authorization service") 1012 w.WriteHeader(http.StatusUnauthorized) 1013 return 1014 } 1015 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username2+":"+password2)) 1016 if auth := r.Header.Get("Authorization"); auth != header { 1017 t.Errorf("unexpected auth: got %s, want %s", auth, header) 1018 w.WriteHeader(http.StatusUnauthorized) 1019 return 1020 } 1021 if got := r.URL.Query().Get("service"); got != service2 { 1022 t.Errorf("unexpected service: got %s, want %s", got, service2) 1023 w.WriteHeader(http.StatusUnauthorized) 1024 return 1025 } 1026 if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes2) { 1027 t.Errorf("unexpected scope: got %s, want %s", got, scopes2) 1028 w.WriteHeader(http.StatusUnauthorized) 1029 return 1030 } 1031 1032 atomic.AddInt64(&authCount2, 1) 1033 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil { 1034 t.Errorf("failed to write %q: %v", r.URL, err) 1035 } 1036 })) 1037 defer as1.Close() 1038 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1039 atomic.AddInt64(&requestCount2, 1) 1040 if r.Method != http.MethodGet || r.URL.Path != "/" { 1041 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 1042 w.WriteHeader(http.StatusNotFound) 1043 return 1044 } 1045 header := "Bearer " + accessToken2 1046 if auth := r.Header.Get("Authorization"); auth != header { 1047 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " ")) 1048 w.Header().Set("Www-Authenticate", challenge) 1049 w.WriteHeader(http.StatusUnauthorized) 1050 return 1051 } 1052 atomic.AddInt64(&successCount2, 1) 1053 })) 1054 defer ts2.Close() 1055 uri2, err := url.Parse(ts2.URL) 1056 if err != nil { 1057 t.Fatalf("invalid test http server: %v", err) 1058 } 1059 service2 = uri2.Host 1060 client2 := &Client{ 1061 Credential: StaticCredential(uri2.Host, Credential{ 1062 Username: username2, 1063 Password: password2, 1064 }), 1065 Cache: NewCache(), 1066 } 1067 1068 ctx := context.Background() 1069 ctx = WithScopesForHost(ctx, uri1.Host, scopes1...) 1070 ctx = WithScopesForHost(ctx, uri2.Host, scopes2...) 1071 // first request to server 1 1072 req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 1073 if err != nil { 1074 t.Fatalf("failed to create test request: %v", err) 1075 } 1076 resp1, err := client1.Do(req1) 1077 if err != nil { 1078 t.Fatalf("Client.Do() error = %v", err) 1079 } 1080 if resp1.StatusCode != http.StatusOK { 1081 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 1082 } 1083 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 1084 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 1085 } 1086 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 1087 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 1088 } 1089 if wantAuthCount1++; authCount1 != wantAuthCount1 { 1090 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 1091 } 1092 1093 // first request to server 2 1094 req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 1095 if err != nil { 1096 t.Fatalf("failed to create test request: %v", err) 1097 } 1098 resp2, err := client2.Do(req2) 1099 if err != nil { 1100 t.Fatalf("Client.Do() error = %v", err) 1101 } 1102 if resp2.StatusCode != http.StatusOK { 1103 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 1104 } 1105 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 1106 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 1107 } 1108 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 1109 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 1110 } 1111 if wantAuthCount2++; authCount2 != wantAuthCount2 { 1112 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 1113 } 1114 1115 // repeated request to server 1 1116 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 1117 if err != nil { 1118 t.Fatalf("failed to create test request: %v", err) 1119 } 1120 resp1, err = client1.Do(req1) 1121 if err != nil { 1122 t.Fatalf("Client.Do() error = %v", err) 1123 } 1124 if resp1.StatusCode != http.StatusOK { 1125 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 1126 } 1127 if wantRequestCount1++; requestCount1 != wantRequestCount1 { 1128 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 1129 } 1130 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 1131 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 1132 } 1133 if authCount1 != wantAuthCount1 { 1134 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 1135 } 1136 1137 // repeated request to server 2 1138 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 1139 if err != nil { 1140 t.Fatalf("failed to create test request: %v", err) 1141 } 1142 resp2, err = client2.Do(req2) 1143 if err != nil { 1144 t.Fatalf("Client.Do() error = %v", err) 1145 } 1146 if resp2.StatusCode != http.StatusOK { 1147 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 1148 } 1149 if wantRequestCount2++; requestCount2 != wantRequestCount2 { 1150 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 1151 } 1152 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 1153 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 1154 } 1155 if authCount2 != wantAuthCount2 { 1156 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 1157 } 1158 1159 // credential change for server 1 1160 username1 = "test_user1_new" 1161 password1 = "test_password1_new" 1162 accessToken1 = "test/access/token/1/new" 1163 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 1164 if err != nil { 1165 t.Fatalf("failed to create test request: %v", err) 1166 } 1167 client1.Credential = StaticCredential(uri1.Host, Credential{ 1168 Username: username1, 1169 Password: password1, 1170 }) 1171 resp1, err = client1.Do(req1) 1172 if err != nil { 1173 t.Fatalf("Client.Do() error = %v", err) 1174 } 1175 if resp1.StatusCode != http.StatusOK { 1176 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 1177 } 1178 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 1179 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 1180 } 1181 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 1182 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 1183 } 1184 if wantAuthCount1++; authCount1 != wantAuthCount1 { 1185 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 1186 } 1187 1188 // credential change for server 2 1189 username2 = "test_user2_new" 1190 password2 = "test_password2_new" 1191 accessToken2 = "test/access/token/2/new" 1192 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 1193 if err != nil { 1194 t.Fatalf("failed to create test request: %v", err) 1195 } 1196 client2.Credential = StaticCredential(uri2.Host, Credential{ 1197 Username: username2, 1198 Password: password2, 1199 }) 1200 resp2, err = client2.Do(req2) 1201 if err != nil { 1202 t.Fatalf("Client.Do() error = %v", err) 1203 } 1204 if resp2.StatusCode != http.StatusOK { 1205 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 1206 } 1207 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 1208 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 1209 } 1210 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 1211 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 1212 } 1213 if wantAuthCount2++; authCount2 != wantAuthCount2 { 1214 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 1215 } 1216 } 1217 1218 func TestClient_Do_Bearer_OAuth2_Password(t *testing.T) { 1219 username := "test_user" 1220 password := "test_password" 1221 accessToken := "test/access/token" 1222 var requestCount, wantRequestCount int64 1223 var successCount, wantSuccessCount int64 1224 var authCount, wantAuthCount int64 1225 var service string 1226 scopes := []string{ 1227 "repository:dst:pull,push", 1228 "repository:src:pull", 1229 } 1230 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1231 if r.Method != http.MethodPost || r.URL.Path != "/" { 1232 t.Error("unexecuted attempt of authorization service") 1233 w.WriteHeader(http.StatusUnauthorized) 1234 return 1235 } 1236 if err := r.ParseForm(); err != nil { 1237 t.Errorf("failed to parse form: %v", err) 1238 w.WriteHeader(http.StatusUnauthorized) 1239 return 1240 } 1241 if got := r.PostForm.Get("grant_type"); got != "password" { 1242 t.Errorf("unexpected grant type: %v, want %v", got, "password") 1243 w.WriteHeader(http.StatusUnauthorized) 1244 return 1245 } 1246 if got := r.PostForm.Get("service"); got != service { 1247 t.Errorf("unexpected service: %v, want %v", got, service) 1248 w.WriteHeader(http.StatusUnauthorized) 1249 return 1250 } 1251 if got := r.PostForm.Get("client_id"); got != defaultClientID { 1252 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 1253 w.WriteHeader(http.StatusUnauthorized) 1254 return 1255 } 1256 scope := strings.Join(scopes, " ") 1257 if got := r.PostForm.Get("scope"); got != scope { 1258 t.Errorf("unexpected scope: %v, want %v", got, scope) 1259 w.WriteHeader(http.StatusUnauthorized) 1260 return 1261 } 1262 if got := r.PostForm.Get("username"); got != username { 1263 t.Errorf("unexpected username: %v, want %v", got, username) 1264 w.WriteHeader(http.StatusUnauthorized) 1265 return 1266 } 1267 if got := r.PostForm.Get("password"); got != password { 1268 t.Errorf("unexpected password: %v, want %v", got, password) 1269 w.WriteHeader(http.StatusUnauthorized) 1270 return 1271 } 1272 1273 atomic.AddInt64(&authCount, 1) 1274 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 1275 t.Errorf("failed to write %q: %v", r.URL, err) 1276 } 1277 })) 1278 defer as.Close() 1279 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1280 atomic.AddInt64(&requestCount, 1) 1281 if r.Method != http.MethodGet || r.URL.Path != "/" { 1282 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 1283 w.WriteHeader(http.StatusNotFound) 1284 return 1285 } 1286 header := "Bearer " + accessToken 1287 if auth := r.Header.Get("Authorization"); auth != header { 1288 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 1289 w.Header().Set("Www-Authenticate", challenge) 1290 w.WriteHeader(http.StatusUnauthorized) 1291 return 1292 } 1293 atomic.AddInt64(&successCount, 1) 1294 })) 1295 defer ts.Close() 1296 uri, err := url.Parse(ts.URL) 1297 if err != nil { 1298 t.Fatalf("invalid test http server: %v", err) 1299 } 1300 service = uri.Host 1301 1302 client := &Client{ 1303 Credential: func(ctx context.Context, reg string) (Credential, error) { 1304 if reg != uri.Host { 1305 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 1306 t.Error(err) 1307 return EmptyCredential, err 1308 } 1309 return Credential{ 1310 Username: username, 1311 Password: password, 1312 }, nil 1313 }, 1314 ForceAttemptOAuth2: true, 1315 } 1316 1317 // first request 1318 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 1319 if err != nil { 1320 t.Fatalf("failed to create test request: %v", err) 1321 } 1322 resp, err := client.Do(req) 1323 if err != nil { 1324 t.Fatalf("Client.Do() error = %v", err) 1325 } 1326 if resp.StatusCode != http.StatusOK { 1327 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 1328 } 1329 if wantRequestCount += 2; requestCount != wantRequestCount { 1330 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 1331 } 1332 if wantSuccessCount++; successCount != wantSuccessCount { 1333 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 1334 } 1335 if wantAuthCount++; authCount != wantAuthCount { 1336 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 1337 } 1338 1339 // credential change 1340 username = "test_user2" 1341 password = "test_password2" 1342 accessToken = "test/access/token/2" 1343 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 1344 if err != nil { 1345 t.Fatalf("failed to create test request: %v", err) 1346 } 1347 resp, err = client.Do(req) 1348 if err != nil { 1349 t.Fatalf("Client.Do() error = %v", err) 1350 } 1351 if resp.StatusCode != http.StatusOK { 1352 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 1353 } 1354 if wantRequestCount += 2; requestCount != wantRequestCount { 1355 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 1356 } 1357 if wantSuccessCount++; successCount != wantSuccessCount { 1358 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 1359 } 1360 if wantAuthCount++; authCount != wantAuthCount { 1361 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 1362 } 1363 } 1364 1365 func TestClient_Do_Bearer_OAuth2_Password_Cached(t *testing.T) { 1366 username := "test_user" 1367 password := "test_password" 1368 accessToken := "test/access/token" 1369 var requestCount, wantRequestCount int64 1370 var successCount, wantSuccessCount int64 1371 var authCount, wantAuthCount int64 1372 var service string 1373 scopes := []string{ 1374 "repository:dst:pull,push", 1375 "repository:src:pull", 1376 } 1377 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1378 if r.Method != http.MethodPost || r.URL.Path != "/" { 1379 t.Error("unexecuted attempt of authorization service") 1380 w.WriteHeader(http.StatusUnauthorized) 1381 return 1382 } 1383 if err := r.ParseForm(); err != nil { 1384 t.Errorf("failed to parse form: %v", err) 1385 w.WriteHeader(http.StatusUnauthorized) 1386 return 1387 } 1388 if got := r.PostForm.Get("grant_type"); got != "password" { 1389 t.Errorf("unexpected grant type: %v, want %v", got, "password") 1390 w.WriteHeader(http.StatusUnauthorized) 1391 return 1392 } 1393 if got := r.PostForm.Get("service"); got != service { 1394 t.Errorf("unexpected service: %v, want %v", got, service) 1395 w.WriteHeader(http.StatusUnauthorized) 1396 return 1397 } 1398 if got := r.PostForm.Get("client_id"); got != defaultClientID { 1399 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 1400 w.WriteHeader(http.StatusUnauthorized) 1401 return 1402 } 1403 scope := strings.Join(scopes, " ") 1404 if got := r.PostForm.Get("scope"); got != scope { 1405 t.Errorf("unexpected scope: %v, want %v", got, scope) 1406 w.WriteHeader(http.StatusUnauthorized) 1407 return 1408 } 1409 if got := r.PostForm.Get("username"); got != username { 1410 t.Errorf("unexpected username: %v, want %v", got, username) 1411 w.WriteHeader(http.StatusUnauthorized) 1412 return 1413 } 1414 if got := r.PostForm.Get("password"); got != password { 1415 t.Errorf("unexpected password: %v, want %v", got, password) 1416 w.WriteHeader(http.StatusUnauthorized) 1417 return 1418 } 1419 1420 atomic.AddInt64(&authCount, 1) 1421 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 1422 t.Errorf("failed to write %q: %v", r.URL, err) 1423 } 1424 })) 1425 defer as.Close() 1426 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1427 atomic.AddInt64(&requestCount, 1) 1428 if r.Method != http.MethodGet || r.URL.Path != "/" { 1429 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 1430 w.WriteHeader(http.StatusNotFound) 1431 return 1432 } 1433 header := "Bearer " + accessToken 1434 if auth := r.Header.Get("Authorization"); auth != header { 1435 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 1436 w.Header().Set("Www-Authenticate", challenge) 1437 w.WriteHeader(http.StatusUnauthorized) 1438 return 1439 } 1440 atomic.AddInt64(&successCount, 1) 1441 })) 1442 defer ts.Close() 1443 uri, err := url.Parse(ts.URL) 1444 if err != nil { 1445 t.Fatalf("invalid test http server: %v", err) 1446 } 1447 service = uri.Host 1448 1449 client := &Client{ 1450 Credential: func(ctx context.Context, reg string) (Credential, error) { 1451 if reg != uri.Host { 1452 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 1453 t.Error(err) 1454 return EmptyCredential, err 1455 } 1456 return Credential{ 1457 Username: username, 1458 Password: password, 1459 }, nil 1460 }, 1461 ForceAttemptOAuth2: true, 1462 Cache: NewCache(), 1463 } 1464 1465 // first request 1466 ctx := WithScopes(context.Background(), scopes...) 1467 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 1468 if err != nil { 1469 t.Fatalf("failed to create test request: %v", err) 1470 } 1471 resp, err := client.Do(req) 1472 if err != nil { 1473 t.Fatalf("Client.Do() error = %v", err) 1474 } 1475 if resp.StatusCode != http.StatusOK { 1476 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 1477 } 1478 if wantRequestCount += 2; requestCount != wantRequestCount { 1479 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 1480 } 1481 if wantSuccessCount++; successCount != wantSuccessCount { 1482 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 1483 } 1484 if wantAuthCount++; authCount != wantAuthCount { 1485 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 1486 } 1487 1488 // repeated request 1489 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 1490 if err != nil { 1491 t.Fatalf("failed to create test request: %v", err) 1492 } 1493 resp, err = client.Do(req) 1494 if err != nil { 1495 t.Fatalf("Client.Do() error = %v", err) 1496 } 1497 if resp.StatusCode != http.StatusOK { 1498 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 1499 } 1500 if wantRequestCount++; requestCount != wantRequestCount { 1501 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 1502 } 1503 if wantSuccessCount++; successCount != wantSuccessCount { 1504 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 1505 } 1506 if authCount != wantAuthCount { 1507 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 1508 } 1509 1510 // credential change 1511 username = "test_user2" 1512 password = "test_password2" 1513 accessToken = "test/access/token/2" 1514 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 1515 if err != nil { 1516 t.Fatalf("failed to create test request: %v", err) 1517 } 1518 resp, err = client.Do(req) 1519 if err != nil { 1520 t.Fatalf("Client.Do() error = %v", err) 1521 } 1522 if resp.StatusCode != http.StatusOK { 1523 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 1524 } 1525 if wantRequestCount += 2; requestCount != wantRequestCount { 1526 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 1527 } 1528 if wantSuccessCount++; successCount != wantSuccessCount { 1529 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 1530 } 1531 if wantAuthCount++; authCount != wantAuthCount { 1532 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 1533 } 1534 } 1535 1536 func TestClient_Do_Bearer_OAuth2_Password_Cached_PerHost(t *testing.T) { 1537 // set up server 1 1538 username1 := "test_user1" 1539 password1 := "test_password1" 1540 accessToken1 := "test/access/token/1" 1541 var requestCount1, wantRequestCount1 int64 1542 var successCount1, wantSuccessCount1 int64 1543 var authCount1, wantAuthCount1 int64 1544 var service1 string 1545 scopes1 := []string{ 1546 "repository:src:pull", 1547 } 1548 as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1549 if r.Method != http.MethodPost || r.URL.Path != "/" { 1550 t.Error("unexecuted attempt of authorization service") 1551 w.WriteHeader(http.StatusUnauthorized) 1552 return 1553 } 1554 if err := r.ParseForm(); err != nil { 1555 t.Errorf("failed to parse form: %v", err) 1556 w.WriteHeader(http.StatusUnauthorized) 1557 return 1558 } 1559 if got := r.PostForm.Get("grant_type"); got != "password" { 1560 t.Errorf("unexpected grant type: %v, want %v", got, "password") 1561 w.WriteHeader(http.StatusUnauthorized) 1562 return 1563 } 1564 if got := r.PostForm.Get("service"); got != service1 { 1565 t.Errorf("unexpected service: %v, want %v", got, service1) 1566 w.WriteHeader(http.StatusUnauthorized) 1567 return 1568 } 1569 if got := r.PostForm.Get("client_id"); got != defaultClientID { 1570 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 1571 w.WriteHeader(http.StatusUnauthorized) 1572 return 1573 } 1574 scope := strings.Join(scopes1, " ") 1575 if got := r.PostForm.Get("scope"); got != scope { 1576 t.Errorf("unexpected scope: %v, want %v", got, scope) 1577 w.WriteHeader(http.StatusUnauthorized) 1578 return 1579 } 1580 if got := r.PostForm.Get("username"); got != username1 { 1581 t.Errorf("unexpected username: %v, want %v", got, username1) 1582 w.WriteHeader(http.StatusUnauthorized) 1583 return 1584 } 1585 if got := r.PostForm.Get("password"); got != password1 { 1586 t.Errorf("unexpected password: %v, want %v", got, password1) 1587 w.WriteHeader(http.StatusUnauthorized) 1588 return 1589 } 1590 1591 atomic.AddInt64(&authCount1, 1) 1592 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil { 1593 t.Errorf("failed to write %q: %v", r.URL, err) 1594 } 1595 })) 1596 defer as1.Close() 1597 ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1598 atomic.AddInt64(&requestCount1, 1) 1599 if r.Method != http.MethodGet || r.URL.Path != "/" { 1600 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 1601 w.WriteHeader(http.StatusNotFound) 1602 return 1603 } 1604 header := "Bearer " + accessToken1 1605 if auth := r.Header.Get("Authorization"); auth != header { 1606 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " ")) 1607 w.Header().Set("Www-Authenticate", challenge) 1608 w.WriteHeader(http.StatusUnauthorized) 1609 return 1610 } 1611 atomic.AddInt64(&successCount1, 1) 1612 })) 1613 defer ts1.Close() 1614 uri1, err := url.Parse(ts1.URL) 1615 if err != nil { 1616 t.Fatalf("invalid test http server: %v", err) 1617 } 1618 service1 = uri1.Host 1619 client1 := &Client{ 1620 Credential: StaticCredential(uri1.Host, Credential{ 1621 Username: username1, 1622 Password: password1, 1623 }), 1624 ForceAttemptOAuth2: true, 1625 Cache: NewCache(), 1626 } 1627 // set up server 2 1628 username2 := "test_user2" 1629 password2 := "test_password2" 1630 accessToken2 := "test/access/token/2" 1631 var requestCount2, wantRequestCount2 int64 1632 var successCount2, wantSuccessCount2 int64 1633 var authCount2, wantAuthCount2 int64 1634 var service2 string 1635 scopes2 := []string{ 1636 "repository:dst:pull,push", 1637 } 1638 as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1639 if r.Method != http.MethodPost || r.URL.Path != "/" { 1640 t.Error("unexecuted attempt of authorization service") 1641 w.WriteHeader(http.StatusUnauthorized) 1642 return 1643 } 1644 if err := r.ParseForm(); err != nil { 1645 t.Errorf("failed to parse form: %v", err) 1646 w.WriteHeader(http.StatusUnauthorized) 1647 return 1648 } 1649 if got := r.PostForm.Get("grant_type"); got != "password" { 1650 t.Errorf("unexpected grant type: %v, want %v", got, "password") 1651 w.WriteHeader(http.StatusUnauthorized) 1652 return 1653 } 1654 if got := r.PostForm.Get("service"); got != service2 { 1655 t.Errorf("unexpected service: %v, want %v", got, service2) 1656 w.WriteHeader(http.StatusUnauthorized) 1657 return 1658 } 1659 if got := r.PostForm.Get("client_id"); got != defaultClientID { 1660 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 1661 w.WriteHeader(http.StatusUnauthorized) 1662 return 1663 } 1664 scope := strings.Join(scopes2, " ") 1665 if got := r.PostForm.Get("scope"); got != scope { 1666 t.Errorf("unexpected scope: %v, want %v", got, scope) 1667 w.WriteHeader(http.StatusUnauthorized) 1668 return 1669 } 1670 if got := r.PostForm.Get("username"); got != username2 { 1671 t.Errorf("unexpected username: %v, want %v", got, username2) 1672 w.WriteHeader(http.StatusUnauthorized) 1673 return 1674 } 1675 if got := r.PostForm.Get("password"); got != password2 { 1676 t.Errorf("unexpected password: %v, want %v", got, password2) 1677 w.WriteHeader(http.StatusUnauthorized) 1678 return 1679 } 1680 1681 atomic.AddInt64(&authCount2, 1) 1682 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil { 1683 t.Errorf("failed to write %q: %v", r.URL, err) 1684 } 1685 })) 1686 defer as2.Close() 1687 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1688 atomic.AddInt64(&requestCount2, 1) 1689 if r.Method != http.MethodGet || r.URL.Path != "/" { 1690 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 1691 w.WriteHeader(http.StatusNotFound) 1692 return 1693 } 1694 header := "Bearer " + accessToken2 1695 if auth := r.Header.Get("Authorization"); auth != header { 1696 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " ")) 1697 w.Header().Set("Www-Authenticate", challenge) 1698 w.WriteHeader(http.StatusUnauthorized) 1699 return 1700 } 1701 atomic.AddInt64(&successCount2, 1) 1702 })) 1703 defer ts2.Close() 1704 uri2, err := url.Parse(ts2.URL) 1705 if err != nil { 1706 t.Fatalf("invalid test http server: %v", err) 1707 } 1708 service2 = uri2.Host 1709 client2 := &Client{ 1710 Credential: StaticCredential(uri2.Host, Credential{ 1711 Username: username2, 1712 Password: password2, 1713 }), 1714 ForceAttemptOAuth2: true, 1715 Cache: NewCache(), 1716 } 1717 1718 ctx := context.Background() 1719 ctx = WithScopesForHost(ctx, uri1.Host, scopes1...) 1720 ctx = WithScopesForHost(ctx, uri2.Host, scopes2...) 1721 // first request to server 1 1722 req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 1723 if err != nil { 1724 t.Fatalf("failed to create test request: %v", err) 1725 } 1726 resp1, err := client1.Do(req1) 1727 if err != nil { 1728 t.Fatalf("Client.Do() error = %v", err) 1729 } 1730 if resp1.StatusCode != http.StatusOK { 1731 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 1732 } 1733 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 1734 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 1735 } 1736 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 1737 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 1738 } 1739 if wantAuthCount1++; authCount1 != wantAuthCount1 { 1740 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 1741 } 1742 // first request to server 2 1743 req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 1744 if err != nil { 1745 t.Fatalf("failed to create test request: %v", err) 1746 } 1747 resp2, err := client2.Do(req2) 1748 if err != nil { 1749 t.Fatalf("Client.Do() error = %v", err) 1750 } 1751 if resp2.StatusCode != http.StatusOK { 1752 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 1753 } 1754 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 1755 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 1756 } 1757 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 1758 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 1759 } 1760 if wantAuthCount2++; authCount2 != wantAuthCount2 { 1761 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 1762 } 1763 1764 // repeated request to server 1 1765 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 1766 if err != nil { 1767 t.Fatalf("failed to create test request: %v", err) 1768 } 1769 resp1, err = client1.Do(req1) 1770 if err != nil { 1771 t.Fatalf("Client.Do() error = %v", err) 1772 } 1773 if resp1.StatusCode != http.StatusOK { 1774 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 1775 } 1776 if wantRequestCount1++; requestCount1 != wantRequestCount1 { 1777 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 1778 } 1779 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 1780 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 1781 } 1782 if authCount1 != wantAuthCount1 { 1783 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 1784 } 1785 // repeated request to server 2 1786 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 1787 if err != nil { 1788 t.Fatalf("failed to create test request: %v", err) 1789 } 1790 resp2, err = client2.Do(req2) 1791 if err != nil { 1792 t.Fatalf("Client.Do() error = %v", err) 1793 } 1794 if resp2.StatusCode != http.StatusOK { 1795 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 1796 } 1797 if wantRequestCount2++; requestCount2 != wantRequestCount2 { 1798 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 1799 } 1800 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 1801 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 1802 } 1803 if authCount2 != wantAuthCount2 { 1804 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 1805 } 1806 1807 // credential change for server 1 1808 username1 = "test_user1_new" 1809 password1 = "test_password1_new" 1810 accessToken1 = "test/access/token/1/new" 1811 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 1812 if err != nil { 1813 t.Fatalf("failed to create test request: %v", err) 1814 } 1815 client1.Credential = StaticCredential(uri1.Host, Credential{ 1816 Username: username1, 1817 Password: password1, 1818 }) 1819 resp1, err = client1.Do(req1) 1820 if err != nil { 1821 t.Fatalf("Client.Do() error = %v", err) 1822 } 1823 if resp1.StatusCode != http.StatusOK { 1824 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 1825 } 1826 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 1827 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 1828 } 1829 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 1830 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 1831 } 1832 if wantAuthCount1++; authCount1 != wantAuthCount1 { 1833 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 1834 } 1835 // credential change for server 2 1836 username2 = "test_user2_new" 1837 password2 = "test_password2_new" 1838 accessToken2 = "test/access/token/2/new" 1839 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 1840 if err != nil { 1841 t.Fatalf("failed to create test request: %v", err) 1842 } 1843 client2.Credential = StaticCredential(uri2.Host, Credential{ 1844 Username: username2, 1845 Password: password2, 1846 }) 1847 resp2, err = client2.Do(req2) 1848 if err != nil { 1849 t.Fatalf("Client.Do() error = %v", err) 1850 } 1851 if resp2.StatusCode != http.StatusOK { 1852 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 1853 } 1854 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 1855 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 1856 } 1857 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 1858 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 1859 } 1860 if wantAuthCount2++; authCount2 != wantAuthCount2 { 1861 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 1862 } 1863 } 1864 1865 func TestClient_Do_Bearer_OAuth2_RefreshToken(t *testing.T) { 1866 refreshToken := "test/refresh/token" 1867 accessToken := "test/access/token" 1868 var requestCount, wantRequestCount int64 1869 var successCount, wantSuccessCount int64 1870 var authCount, wantAuthCount int64 1871 var service string 1872 scopes := []string{ 1873 "repository:dst:pull,push", 1874 "repository:src:pull", 1875 } 1876 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1877 if r.Method != http.MethodPost || r.URL.Path != "/" { 1878 t.Error("unexecuted attempt of authorization service") 1879 w.WriteHeader(http.StatusUnauthorized) 1880 return 1881 } 1882 if err := r.ParseForm(); err != nil { 1883 t.Errorf("failed to parse form: %v", err) 1884 w.WriteHeader(http.StatusUnauthorized) 1885 return 1886 } 1887 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 1888 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token") 1889 w.WriteHeader(http.StatusUnauthorized) 1890 return 1891 } 1892 if got := r.PostForm.Get("service"); got != service { 1893 t.Errorf("unexpected service: %v, want %v", got, service) 1894 w.WriteHeader(http.StatusUnauthorized) 1895 return 1896 } 1897 if got := r.PostForm.Get("client_id"); got != defaultClientID { 1898 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 1899 w.WriteHeader(http.StatusUnauthorized) 1900 return 1901 } 1902 scope := strings.Join(scopes, " ") 1903 if got := r.PostForm.Get("scope"); got != scope { 1904 t.Errorf("unexpected scope: %v, want %v", got, scope) 1905 w.WriteHeader(http.StatusUnauthorized) 1906 return 1907 } 1908 if got := r.PostForm.Get("refresh_token"); got != refreshToken { 1909 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken) 1910 w.WriteHeader(http.StatusUnauthorized) 1911 return 1912 } 1913 1914 atomic.AddInt64(&authCount, 1) 1915 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 1916 t.Errorf("failed to write %q: %v", r.URL, err) 1917 } 1918 })) 1919 defer as.Close() 1920 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1921 atomic.AddInt64(&requestCount, 1) 1922 if r.Method != http.MethodGet || r.URL.Path != "/" { 1923 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 1924 w.WriteHeader(http.StatusNotFound) 1925 return 1926 } 1927 header := "Bearer " + accessToken 1928 if auth := r.Header.Get("Authorization"); auth != header { 1929 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 1930 w.Header().Set("Www-Authenticate", challenge) 1931 w.WriteHeader(http.StatusUnauthorized) 1932 return 1933 } 1934 atomic.AddInt64(&successCount, 1) 1935 })) 1936 defer ts.Close() 1937 uri, err := url.Parse(ts.URL) 1938 if err != nil { 1939 t.Fatalf("invalid test http server: %v", err) 1940 } 1941 service = uri.Host 1942 1943 client := &Client{ 1944 Credential: func(ctx context.Context, reg string) (Credential, error) { 1945 if reg != uri.Host { 1946 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 1947 t.Error(err) 1948 return EmptyCredential, err 1949 } 1950 return Credential{ 1951 RefreshToken: refreshToken, 1952 }, nil 1953 }, 1954 } 1955 1956 // first request 1957 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 1958 if err != nil { 1959 t.Fatalf("failed to create test request: %v", err) 1960 } 1961 resp, err := client.Do(req) 1962 if err != nil { 1963 t.Fatalf("Client.Do() error = %v", err) 1964 } 1965 if resp.StatusCode != http.StatusOK { 1966 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 1967 } 1968 if wantRequestCount += 2; requestCount != wantRequestCount { 1969 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 1970 } 1971 if wantSuccessCount++; successCount != wantSuccessCount { 1972 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 1973 } 1974 if wantAuthCount++; authCount != wantAuthCount { 1975 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 1976 } 1977 1978 // credential change 1979 refreshToken = "test/refresh/token/2" 1980 accessToken = "test/access/token/2" 1981 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 1982 if err != nil { 1983 t.Fatalf("failed to create test request: %v", err) 1984 } 1985 resp, err = client.Do(req) 1986 if err != nil { 1987 t.Fatalf("Client.Do() error = %v", err) 1988 } 1989 if resp.StatusCode != http.StatusOK { 1990 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 1991 } 1992 if wantRequestCount += 2; requestCount != wantRequestCount { 1993 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 1994 } 1995 if wantSuccessCount++; successCount != wantSuccessCount { 1996 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 1997 } 1998 if wantAuthCount++; authCount != wantAuthCount { 1999 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 2000 } 2001 } 2002 2003 func TestClient_Do_Bearer_OAuth2_RefreshToken_Cached(t *testing.T) { 2004 refreshToken := "test/refresh/token" 2005 accessToken := "test/access/token" 2006 var requestCount, wantRequestCount int64 2007 var successCount, wantSuccessCount int64 2008 var authCount, wantAuthCount int64 2009 var service string 2010 scopes := []string{ 2011 "repository:dst:pull,push", 2012 "repository:src:pull", 2013 } 2014 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2015 if r.Method != http.MethodPost || r.URL.Path != "/" { 2016 t.Error("unexecuted attempt of authorization service") 2017 w.WriteHeader(http.StatusUnauthorized) 2018 return 2019 } 2020 if err := r.ParseForm(); err != nil { 2021 t.Errorf("failed to parse form: %v", err) 2022 w.WriteHeader(http.StatusUnauthorized) 2023 return 2024 } 2025 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 2026 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token") 2027 w.WriteHeader(http.StatusUnauthorized) 2028 return 2029 } 2030 if got := r.PostForm.Get("service"); got != service { 2031 t.Errorf("unexpected service: %v, want %v", got, service) 2032 w.WriteHeader(http.StatusUnauthorized) 2033 return 2034 } 2035 if got := r.PostForm.Get("client_id"); got != defaultClientID { 2036 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 2037 w.WriteHeader(http.StatusUnauthorized) 2038 return 2039 } 2040 scope := strings.Join(scopes, " ") 2041 if got := r.PostForm.Get("scope"); got != scope { 2042 t.Errorf("unexpected scope: %v, want %v", got, scope) 2043 w.WriteHeader(http.StatusUnauthorized) 2044 return 2045 } 2046 if got := r.PostForm.Get("refresh_token"); got != refreshToken { 2047 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken) 2048 w.WriteHeader(http.StatusUnauthorized) 2049 return 2050 } 2051 2052 atomic.AddInt64(&authCount, 1) 2053 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 2054 t.Errorf("failed to write %q: %v", r.URL, err) 2055 } 2056 })) 2057 defer as.Close() 2058 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2059 atomic.AddInt64(&requestCount, 1) 2060 if r.Method != http.MethodGet || r.URL.Path != "/" { 2061 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 2062 w.WriteHeader(http.StatusNotFound) 2063 return 2064 } 2065 header := "Bearer " + accessToken 2066 if auth := r.Header.Get("Authorization"); auth != header { 2067 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 2068 w.Header().Set("Www-Authenticate", challenge) 2069 w.WriteHeader(http.StatusUnauthorized) 2070 return 2071 } 2072 atomic.AddInt64(&successCount, 1) 2073 })) 2074 defer ts.Close() 2075 uri, err := url.Parse(ts.URL) 2076 if err != nil { 2077 t.Fatalf("invalid test http server: %v", err) 2078 } 2079 service = uri.Host 2080 2081 client := &Client{ 2082 Credential: func(ctx context.Context, reg string) (Credential, error) { 2083 if reg != uri.Host { 2084 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 2085 t.Error(err) 2086 return EmptyCredential, err 2087 } 2088 return Credential{ 2089 RefreshToken: refreshToken, 2090 }, nil 2091 }, 2092 Cache: NewCache(), 2093 } 2094 2095 // first request 2096 ctx := WithScopes(context.Background(), scopes...) 2097 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 2098 if err != nil { 2099 t.Fatalf("failed to create test request: %v", err) 2100 } 2101 resp, err := client.Do(req) 2102 if err != nil { 2103 t.Fatalf("Client.Do() error = %v", err) 2104 } 2105 if resp.StatusCode != http.StatusOK { 2106 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 2107 } 2108 if wantRequestCount += 2; requestCount != wantRequestCount { 2109 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 2110 } 2111 if wantSuccessCount++; successCount != wantSuccessCount { 2112 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 2113 } 2114 if wantAuthCount++; authCount != wantAuthCount { 2115 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 2116 } 2117 2118 // repeated request 2119 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 2120 if err != nil { 2121 t.Fatalf("failed to create test request: %v", err) 2122 } 2123 resp, err = client.Do(req) 2124 if err != nil { 2125 t.Fatalf("Client.Do() error = %v", err) 2126 } 2127 if resp.StatusCode != http.StatusOK { 2128 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 2129 } 2130 if wantRequestCount++; requestCount != wantRequestCount { 2131 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 2132 } 2133 if wantSuccessCount++; successCount != wantSuccessCount { 2134 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 2135 } 2136 if authCount != wantAuthCount { 2137 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 2138 } 2139 2140 // credential change 2141 refreshToken = "test/refresh/token/2" 2142 accessToken = "test/access/token/2" 2143 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 2144 if err != nil { 2145 t.Fatalf("failed to create test request: %v", err) 2146 } 2147 resp, err = client.Do(req) 2148 if err != nil { 2149 t.Fatalf("Client.Do() error = %v", err) 2150 } 2151 if resp.StatusCode != http.StatusOK { 2152 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 2153 } 2154 if wantRequestCount += 2; requestCount != wantRequestCount { 2155 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 2156 } 2157 if wantSuccessCount++; successCount != wantSuccessCount { 2158 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 2159 } 2160 if wantAuthCount++; authCount != wantAuthCount { 2161 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 2162 } 2163 } 2164 2165 func TestClient_Do_Bearer_OAuth2_RefreshToken_Cached_PerHost(t *testing.T) { 2166 // set up server 1 2167 refreshToken1 := "test/refresh/token/1" 2168 accessToken1 := "test/access/token/1" 2169 var requestCount1, wantRequestCount1 int64 2170 var successCount1, wantSuccessCount1 int64 2171 var authCount1, wantAuthCount1 int64 2172 var service1 string 2173 scopes1 := []string{ 2174 "repository:src:pull", 2175 } 2176 as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2177 if r.Method != http.MethodPost || r.URL.Path != "/" { 2178 t.Error("unexecuted attempt of authorization service") 2179 w.WriteHeader(http.StatusUnauthorized) 2180 return 2181 } 2182 if err := r.ParseForm(); err != nil { 2183 t.Errorf("failed to parse form: %v", err) 2184 w.WriteHeader(http.StatusUnauthorized) 2185 return 2186 } 2187 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 2188 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token") 2189 w.WriteHeader(http.StatusUnauthorized) 2190 return 2191 } 2192 if got := r.PostForm.Get("service"); got != service1 { 2193 t.Errorf("unexpected service: %v, want %v", got, service1) 2194 w.WriteHeader(http.StatusUnauthorized) 2195 return 2196 } 2197 if got := r.PostForm.Get("client_id"); got != defaultClientID { 2198 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 2199 w.WriteHeader(http.StatusUnauthorized) 2200 return 2201 } 2202 scope := strings.Join(scopes1, " ") 2203 if got := r.PostForm.Get("scope"); got != scope { 2204 t.Errorf("unexpected scope: %v, want %v", got, scope) 2205 w.WriteHeader(http.StatusUnauthorized) 2206 return 2207 } 2208 if got := r.PostForm.Get("refresh_token"); got != refreshToken1 { 2209 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken1) 2210 w.WriteHeader(http.StatusUnauthorized) 2211 return 2212 } 2213 2214 atomic.AddInt64(&authCount1, 1) 2215 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil { 2216 t.Errorf("failed to write %q: %v", r.URL, err) 2217 } 2218 })) 2219 defer as1.Close() 2220 ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2221 atomic.AddInt64(&requestCount1, 1) 2222 if r.Method != http.MethodGet || r.URL.Path != "/" { 2223 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 2224 w.WriteHeader(http.StatusNotFound) 2225 return 2226 } 2227 header := "Bearer " + accessToken1 2228 if auth := r.Header.Get("Authorization"); auth != header { 2229 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " ")) 2230 w.Header().Set("Www-Authenticate", challenge) 2231 w.WriteHeader(http.StatusUnauthorized) 2232 return 2233 } 2234 atomic.AddInt64(&successCount1, 1) 2235 })) 2236 defer ts1.Close() 2237 uri1, err := url.Parse(ts1.URL) 2238 if err != nil { 2239 t.Fatalf("invalid test http server: %v", err) 2240 } 2241 service1 = uri1.Host 2242 client1 := &Client{ 2243 Credential: StaticCredential(uri1.Host, Credential{ 2244 RefreshToken: refreshToken1, 2245 }), 2246 Cache: NewCache(), 2247 } 2248 2249 // set up server 2 2250 refreshToken2 := "test/refresh/token/1" 2251 accessToken2 := "test/access/token/1" 2252 var requestCount2, wantRequestCount2 int64 2253 var successCount2, wantSuccessCount2 int64 2254 var authCount2, wantAuthCount2 int64 2255 var service2 string 2256 scopes2 := []string{ 2257 "repository:dst:pull,push", 2258 } 2259 as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2260 if r.Method != http.MethodPost || r.URL.Path != "/" { 2261 t.Error("unexecuted attempt of authorization service") 2262 w.WriteHeader(http.StatusUnauthorized) 2263 return 2264 } 2265 if err := r.ParseForm(); err != nil { 2266 t.Errorf("failed to parse form: %v", err) 2267 w.WriteHeader(http.StatusUnauthorized) 2268 return 2269 } 2270 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 2271 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token") 2272 w.WriteHeader(http.StatusUnauthorized) 2273 return 2274 } 2275 if got := r.PostForm.Get("service"); got != service2 { 2276 t.Errorf("unexpected service: %v, want %v", got, service2) 2277 w.WriteHeader(http.StatusUnauthorized) 2278 return 2279 } 2280 if got := r.PostForm.Get("client_id"); got != defaultClientID { 2281 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 2282 w.WriteHeader(http.StatusUnauthorized) 2283 return 2284 } 2285 scope := strings.Join(scopes2, " ") 2286 if got := r.PostForm.Get("scope"); got != scope { 2287 t.Errorf("unexpected scope: %v, want %v", got, scope) 2288 w.WriteHeader(http.StatusUnauthorized) 2289 return 2290 } 2291 if got := r.PostForm.Get("refresh_token"); got != refreshToken2 { 2292 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken2) 2293 w.WriteHeader(http.StatusUnauthorized) 2294 return 2295 } 2296 2297 atomic.AddInt64(&authCount2, 1) 2298 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil { 2299 t.Errorf("failed to write %q: %v", r.URL, err) 2300 } 2301 })) 2302 defer as2.Close() 2303 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2304 atomic.AddInt64(&requestCount2, 1) 2305 if r.Method != http.MethodGet || r.URL.Path != "/" { 2306 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 2307 w.WriteHeader(http.StatusNotFound) 2308 return 2309 } 2310 header := "Bearer " + accessToken2 2311 if auth := r.Header.Get("Authorization"); auth != header { 2312 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " ")) 2313 w.Header().Set("Www-Authenticate", challenge) 2314 w.WriteHeader(http.StatusUnauthorized) 2315 return 2316 } 2317 atomic.AddInt64(&successCount2, 1) 2318 })) 2319 defer ts2.Close() 2320 uri2, err := url.Parse(ts2.URL) 2321 if err != nil { 2322 t.Fatalf("invalid test http server: %v", err) 2323 } 2324 service2 = uri2.Host 2325 client2 := &Client{ 2326 Credential: StaticCredential(uri2.Host, Credential{ 2327 RefreshToken: refreshToken2, 2328 }), 2329 Cache: NewCache(), 2330 } 2331 2332 ctx := context.Background() 2333 ctx = WithScopesForHost(ctx, uri1.Host, scopes1...) 2334 ctx = WithScopesForHost(ctx, uri2.Host, scopes2...) 2335 // first request to server 1 2336 req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 2337 if err != nil { 2338 t.Fatalf("failed to create test request: %v", err) 2339 } 2340 resp1, err := client1.Do(req1) 2341 if err != nil { 2342 t.Fatalf("Client.Do() error = %v", err) 2343 } 2344 if resp1.StatusCode != http.StatusOK { 2345 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 2346 } 2347 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 2348 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 2349 } 2350 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 2351 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 2352 } 2353 if wantAuthCount1++; authCount1 != wantAuthCount1 { 2354 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 2355 } 2356 2357 // first request to server 2 2358 req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 2359 if err != nil { 2360 t.Fatalf("failed to create test request: %v", err) 2361 } 2362 resp2, err := client2.Do(req2) 2363 if err != nil { 2364 t.Fatalf("Client.Do() error = %v", err) 2365 } 2366 if resp2.StatusCode != http.StatusOK { 2367 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 2368 } 2369 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 2370 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 2371 } 2372 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 2373 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 2374 } 2375 if wantAuthCount2++; authCount2 != wantAuthCount2 { 2376 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 2377 } 2378 2379 // repeated request to server 1 2380 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 2381 if err != nil { 2382 t.Fatalf("failed to create test request: %v", err) 2383 } 2384 resp1, err = client1.Do(req1) 2385 if err != nil { 2386 t.Fatalf("Client.Do() error = %v", err) 2387 } 2388 if resp1.StatusCode != http.StatusOK { 2389 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 2390 } 2391 if wantRequestCount1++; requestCount1 != wantRequestCount1 { 2392 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 2393 } 2394 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 2395 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 2396 } 2397 if authCount1 != wantAuthCount1 { 2398 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 2399 } 2400 // repeated request to server 2 2401 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 2402 if err != nil { 2403 t.Fatalf("failed to create test request: %v", err) 2404 } 2405 resp2, err = client2.Do(req2) 2406 if err != nil { 2407 t.Fatalf("Client.Do() error = %v", err) 2408 } 2409 if resp2.StatusCode != http.StatusOK { 2410 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 2411 } 2412 if wantRequestCount2++; requestCount2 != wantRequestCount2 { 2413 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 2414 } 2415 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 2416 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 2417 } 2418 if authCount2 != wantAuthCount2 { 2419 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 2420 } 2421 2422 // credential change to server 1 2423 refreshToken1 = "test/refresh/token/1/new" 2424 accessToken1 = "test/access/token/1/new" 2425 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 2426 if err != nil { 2427 t.Fatalf("failed to create test request: %v", err) 2428 } 2429 client1.Credential = StaticCredential(uri1.Host, Credential{ 2430 RefreshToken: refreshToken1, 2431 }) 2432 resp1, err = client1.Do(req1) 2433 if err != nil { 2434 t.Fatalf("Client.Do() error = %v", err) 2435 } 2436 if resp1.StatusCode != http.StatusOK { 2437 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 2438 } 2439 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 2440 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 2441 } 2442 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 2443 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 2444 } 2445 if wantAuthCount1++; authCount1 != wantAuthCount1 { 2446 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 2447 } 2448 // credential change to server 2 2449 refreshToken2 = "test/refresh/token/2/new" 2450 accessToken2 = "test/access/token/2/new" 2451 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 2452 if err != nil { 2453 t.Fatalf("failed to create test request: %v", err) 2454 } 2455 client2.Credential = StaticCredential(uri2.Host, Credential{ 2456 RefreshToken: refreshToken2, 2457 }) 2458 resp2, err = client2.Do(req2) 2459 if err != nil { 2460 t.Fatalf("Client.Do() error = %v", err) 2461 } 2462 if resp2.StatusCode != http.StatusOK { 2463 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 2464 } 2465 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 2466 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 2467 } 2468 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 2469 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 2470 } 2471 if wantAuthCount2++; authCount2 != wantAuthCount2 { 2472 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 2473 } 2474 } 2475 2476 func TestClient_Do_Token_Expire(t *testing.T) { 2477 refreshToken := "test/refresh/token" 2478 accessToken := "test/access/token" 2479 var requestCount, wantRequestCount int64 2480 var successCount, wantSuccessCount int64 2481 var authCount, wantAuthCount int64 2482 var service string 2483 scopes := []string{ 2484 "repository:dst:pull,push", 2485 "repository:src:pull", 2486 } 2487 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2488 if r.Method != http.MethodPost || r.URL.Path != "/" { 2489 t.Error("unexecuted attempt of authorization service") 2490 w.WriteHeader(http.StatusUnauthorized) 2491 return 2492 } 2493 if err := r.ParseForm(); err != nil { 2494 t.Errorf("failed to parse form: %v", err) 2495 w.WriteHeader(http.StatusUnauthorized) 2496 return 2497 } 2498 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 2499 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token") 2500 w.WriteHeader(http.StatusUnauthorized) 2501 return 2502 } 2503 if got := r.PostForm.Get("service"); got != service { 2504 t.Errorf("unexpected service: %v, want %v", got, service) 2505 w.WriteHeader(http.StatusUnauthorized) 2506 return 2507 } 2508 if got := r.PostForm.Get("client_id"); got != defaultClientID { 2509 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 2510 w.WriteHeader(http.StatusUnauthorized) 2511 return 2512 } 2513 scope := strings.Join(scopes, " ") 2514 if got := r.PostForm.Get("scope"); got != scope { 2515 t.Errorf("unexpected scope: %v, want %v", got, scope) 2516 w.WriteHeader(http.StatusUnauthorized) 2517 return 2518 } 2519 if got := r.PostForm.Get("refresh_token"); got != refreshToken { 2520 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken) 2521 w.WriteHeader(http.StatusUnauthorized) 2522 return 2523 } 2524 2525 atomic.AddInt64(&authCount, 1) 2526 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 2527 t.Errorf("failed to write %q: %v", r.URL, err) 2528 } 2529 })) 2530 defer as.Close() 2531 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2532 atomic.AddInt64(&requestCount, 1) 2533 if r.Method != http.MethodGet || r.URL.Path != "/" { 2534 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 2535 w.WriteHeader(http.StatusNotFound) 2536 return 2537 } 2538 header := "Bearer " + accessToken 2539 if auth := r.Header.Get("Authorization"); auth != header { 2540 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 2541 w.Header().Set("Www-Authenticate", challenge) 2542 w.WriteHeader(http.StatusUnauthorized) 2543 return 2544 } 2545 atomic.AddInt64(&successCount, 1) 2546 })) 2547 defer ts.Close() 2548 uri, err := url.Parse(ts.URL) 2549 if err != nil { 2550 t.Fatalf("invalid test http server: %v", err) 2551 } 2552 service = uri.Host 2553 2554 client := &Client{ 2555 Credential: func(ctx context.Context, reg string) (Credential, error) { 2556 if reg != uri.Host { 2557 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 2558 t.Error(err) 2559 return EmptyCredential, err 2560 } 2561 return Credential{ 2562 RefreshToken: refreshToken, 2563 }, nil 2564 }, 2565 Cache: NewCache(), 2566 } 2567 2568 // first request 2569 ctx := WithScopes(context.Background(), scopes...) 2570 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 2571 if err != nil { 2572 t.Fatalf("failed to create test request: %v", err) 2573 } 2574 resp, err := client.Do(req) 2575 if err != nil { 2576 t.Fatalf("Client.Do() error = %v", err) 2577 } 2578 if resp.StatusCode != http.StatusOK { 2579 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 2580 } 2581 if wantRequestCount += 2; requestCount != wantRequestCount { 2582 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 2583 } 2584 if wantSuccessCount++; successCount != wantSuccessCount { 2585 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 2586 } 2587 if wantAuthCount++; authCount != wantAuthCount { 2588 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 2589 } 2590 2591 // invalidate the access token and request again 2592 accessToken = "test/access/token/2" 2593 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 2594 if err != nil { 2595 t.Fatalf("failed to create test request: %v", err) 2596 } 2597 resp, err = client.Do(req) 2598 if err != nil { 2599 t.Fatalf("Client.Do() error = %v", err) 2600 } 2601 if resp.StatusCode != http.StatusOK { 2602 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 2603 } 2604 if wantRequestCount += 2; requestCount != wantRequestCount { 2605 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 2606 } 2607 if wantSuccessCount++; successCount != wantSuccessCount { 2608 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 2609 } 2610 if wantAuthCount++; authCount != wantAuthCount { 2611 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 2612 } 2613 } 2614 2615 func TestClient_Do_Token_Expire_PerHost(t *testing.T) { 2616 // set up server 1 2617 refreshToken1 := "test/refresh/token/1" 2618 accessToken1 := "test/access/token/1" 2619 var requestCount1, wantRequestCount1 int64 2620 var successCount1, wantSuccessCount1 int64 2621 var authCount1, wantAuthCount1 int64 2622 var service1 string 2623 scopes1 := []string{ 2624 "repository:src:pull", 2625 } 2626 as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2627 if r.Method != http.MethodPost || r.URL.Path != "/" { 2628 t.Error("unexecuted attempt of authorization service") 2629 w.WriteHeader(http.StatusUnauthorized) 2630 return 2631 } 2632 if err := r.ParseForm(); err != nil { 2633 t.Errorf("failed to parse form: %v", err) 2634 w.WriteHeader(http.StatusUnauthorized) 2635 return 2636 } 2637 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 2638 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token") 2639 w.WriteHeader(http.StatusUnauthorized) 2640 return 2641 } 2642 if got := r.PostForm.Get("service"); got != service1 { 2643 t.Errorf("unexpected service: %v, want %v", got, service1) 2644 w.WriteHeader(http.StatusUnauthorized) 2645 return 2646 } 2647 if got := r.PostForm.Get("client_id"); got != defaultClientID { 2648 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 2649 w.WriteHeader(http.StatusUnauthorized) 2650 return 2651 } 2652 scope := strings.Join(scopes1, " ") 2653 if got := r.PostForm.Get("scope"); got != scope { 2654 t.Errorf("unexpected scope: %v, want %v", got, scope) 2655 w.WriteHeader(http.StatusUnauthorized) 2656 return 2657 } 2658 if got := r.PostForm.Get("refresh_token"); got != refreshToken1 { 2659 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken1) 2660 w.WriteHeader(http.StatusUnauthorized) 2661 return 2662 } 2663 2664 atomic.AddInt64(&authCount1, 1) 2665 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil { 2666 t.Errorf("failed to write %q: %v", r.URL, err) 2667 } 2668 })) 2669 defer as1.Close() 2670 ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2671 atomic.AddInt64(&requestCount1, 1) 2672 if r.Method != http.MethodGet || r.URL.Path != "/" { 2673 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 2674 w.WriteHeader(http.StatusNotFound) 2675 return 2676 } 2677 header := "Bearer " + accessToken1 2678 if auth := r.Header.Get("Authorization"); auth != header { 2679 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, strings.Join(scopes1, " ")) 2680 w.Header().Set("Www-Authenticate", challenge) 2681 w.WriteHeader(http.StatusUnauthorized) 2682 return 2683 } 2684 atomic.AddInt64(&successCount1, 1) 2685 })) 2686 defer ts1.Close() 2687 uri1, err := url.Parse(ts1.URL) 2688 if err != nil { 2689 t.Fatalf("invalid test http server: %v", err) 2690 } 2691 service1 = uri1.Host 2692 client1 := &Client{ 2693 Credential: StaticCredential(uri1.Host, Credential{ 2694 RefreshToken: refreshToken1, 2695 }), 2696 Cache: NewCache(), 2697 } 2698 // set up server 2 2699 refreshToken2 := "test/refresh/token/2" 2700 accessToken2 := "test/access/token/2" 2701 var requestCount2, wantRequestCount2 int64 2702 var successCount2, wantSuccessCount2 int64 2703 var authCount2, wantAuthCount2 int64 2704 var service2 string 2705 scopes2 := []string{ 2706 "repository:dst:pull,push", 2707 } 2708 as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2709 if r.Method != http.MethodPost || r.URL.Path != "/" { 2710 t.Error("unexecuted attempt of authorization service") 2711 w.WriteHeader(http.StatusUnauthorized) 2712 return 2713 } 2714 if err := r.ParseForm(); err != nil { 2715 t.Errorf("failed to parse form: %v", err) 2716 w.WriteHeader(http.StatusUnauthorized) 2717 return 2718 } 2719 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 2720 t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token") 2721 w.WriteHeader(http.StatusUnauthorized) 2722 return 2723 } 2724 if got := r.PostForm.Get("service"); got != service2 { 2725 t.Errorf("unexpected service: %v, want %v", got, service2) 2726 w.WriteHeader(http.StatusUnauthorized) 2727 return 2728 } 2729 if got := r.PostForm.Get("client_id"); got != defaultClientID { 2730 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 2731 w.WriteHeader(http.StatusUnauthorized) 2732 return 2733 } 2734 scope := strings.Join(scopes2, " ") 2735 if got := r.PostForm.Get("scope"); got != scope { 2736 t.Errorf("unexpected scope: %v, want %v", got, scope) 2737 w.WriteHeader(http.StatusUnauthorized) 2738 return 2739 } 2740 if got := r.PostForm.Get("refresh_token"); got != refreshToken2 { 2741 t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken2) 2742 w.WriteHeader(http.StatusUnauthorized) 2743 return 2744 } 2745 2746 atomic.AddInt64(&authCount2, 1) 2747 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil { 2748 t.Errorf("failed to write %q: %v", r.URL, err) 2749 } 2750 })) 2751 defer as2.Close() 2752 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2753 atomic.AddInt64(&requestCount2, 1) 2754 if r.Method != http.MethodGet || r.URL.Path != "/" { 2755 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 2756 w.WriteHeader(http.StatusNotFound) 2757 return 2758 } 2759 header := "Bearer " + accessToken2 2760 if auth := r.Header.Get("Authorization"); auth != header { 2761 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, strings.Join(scopes2, " ")) 2762 w.Header().Set("Www-Authenticate", challenge) 2763 w.WriteHeader(http.StatusUnauthorized) 2764 return 2765 } 2766 atomic.AddInt64(&successCount2, 1) 2767 })) 2768 defer ts2.Close() 2769 uri2, err := url.Parse(ts2.URL) 2770 if err != nil { 2771 t.Fatalf("invalid test http server: %v", err) 2772 } 2773 service2 = uri2.Host 2774 client2 := &Client{ 2775 Credential: StaticCredential(uri2.Host, Credential{ 2776 RefreshToken: refreshToken2, 2777 }), 2778 Cache: NewCache(), 2779 } 2780 2781 ctx := context.Background() 2782 ctx = WithScopesForHost(ctx, uri1.Host, scopes1...) 2783 ctx = WithScopesForHost(ctx, uri2.Host, scopes2...) 2784 // first request to server 1 2785 req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 2786 if err != nil { 2787 t.Fatalf("failed to create test request: %v", err) 2788 } 2789 resp1, err := client1.Do(req1) 2790 if err != nil { 2791 t.Fatalf("Client.Do() error = %v", err) 2792 } 2793 if resp1.StatusCode != http.StatusOK { 2794 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 2795 } 2796 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 2797 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 2798 } 2799 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 2800 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 2801 } 2802 if wantAuthCount1++; authCount1 != wantAuthCount1 { 2803 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 2804 } 2805 2806 // first request to server 2 2807 req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 2808 if err != nil { 2809 t.Fatalf("failed to create test request: %v", err) 2810 } 2811 resp2, err := client2.Do(req2) 2812 if err != nil { 2813 t.Fatalf("Client.Do() error = %v", err) 2814 } 2815 if resp2.StatusCode != http.StatusOK { 2816 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 2817 } 2818 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 2819 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 2820 } 2821 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 2822 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 2823 } 2824 if wantAuthCount2++; authCount2 != wantAuthCount2 { 2825 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 2826 } 2827 2828 // invalidate the access token and request again to server 1 2829 accessToken1 = "test/access/token/1/new" 2830 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 2831 if err != nil { 2832 t.Fatalf("failed to create test request: %v", err) 2833 } 2834 resp1, err = client1.Do(req1) 2835 if err != nil { 2836 t.Fatalf("Client.Do() error = %v", err) 2837 } 2838 if resp1.StatusCode != http.StatusOK { 2839 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 2840 } 2841 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 2842 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 2843 } 2844 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 2845 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 2846 } 2847 if wantAuthCount1++; authCount1 != wantAuthCount1 { 2848 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 2849 } 2850 // invalidate the access token and request again to server 2 2851 accessToken2 = "test/access/token/2/new" 2852 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 2853 if err != nil { 2854 t.Fatalf("failed to create test request: %v", err) 2855 } 2856 resp2, err = client2.Do(req2) 2857 if err != nil { 2858 t.Fatalf("Client.Do() error = %v", err) 2859 } 2860 if resp2.StatusCode != http.StatusOK { 2861 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 2862 } 2863 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 2864 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 2865 } 2866 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 2867 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 2868 } 2869 if wantAuthCount2++; authCount2 != wantAuthCount2 { 2870 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 2871 } 2872 } 2873 2874 func TestClient_Do_Scope_Hint_Mismatch(t *testing.T) { 2875 username := "test_user" 2876 password := "test_password" 2877 accessToken := "test/access/token" 2878 var requestCount, wantRequestCount int64 2879 var successCount, wantSuccessCount int64 2880 var authCount, wantAuthCount int64 2881 var service string 2882 scopes := []string{ 2883 "repository:dst:pull,push", 2884 "repository:src:pull", 2885 } 2886 scope := "repository:test:delete" 2887 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2888 if r.Method != http.MethodPost || r.URL.Path != "/" { 2889 t.Error("unexecuted attempt of authorization service") 2890 w.WriteHeader(http.StatusUnauthorized) 2891 return 2892 } 2893 if err := r.ParseForm(); err != nil { 2894 t.Errorf("failed to parse form: %v", err) 2895 w.WriteHeader(http.StatusUnauthorized) 2896 return 2897 } 2898 if got := r.PostForm.Get("grant_type"); got != "password" { 2899 t.Errorf("unexpected grant type: %v, want %v", got, "password") 2900 w.WriteHeader(http.StatusUnauthorized) 2901 return 2902 } 2903 if got := r.PostForm.Get("service"); got != service { 2904 t.Errorf("unexpected service: %v, want %v", got, service) 2905 w.WriteHeader(http.StatusUnauthorized) 2906 return 2907 } 2908 if got := r.PostForm.Get("client_id"); got != defaultClientID { 2909 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 2910 w.WriteHeader(http.StatusUnauthorized) 2911 return 2912 } 2913 scopes := CleanScopes(append([]string{scope}, scopes...)) 2914 scope := strings.Join(scopes, " ") 2915 if got := r.PostForm.Get("scope"); got != scope { 2916 t.Errorf("unexpected scope: %v, want %v", got, scope) 2917 w.WriteHeader(http.StatusUnauthorized) 2918 return 2919 } 2920 if got := r.PostForm.Get("username"); got != username { 2921 t.Errorf("unexpected username: %v, want %v", got, username) 2922 w.WriteHeader(http.StatusUnauthorized) 2923 return 2924 } 2925 if got := r.PostForm.Get("password"); got != password { 2926 t.Errorf("unexpected password: %v, want %v", got, password) 2927 w.WriteHeader(http.StatusUnauthorized) 2928 return 2929 } 2930 2931 atomic.AddInt64(&authCount, 1) 2932 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 2933 t.Errorf("failed to write %q: %v", r.URL, err) 2934 } 2935 })) 2936 defer as.Close() 2937 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2938 atomic.AddInt64(&requestCount, 1) 2939 if r.Method != http.MethodGet || r.URL.Path != "/" { 2940 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 2941 w.WriteHeader(http.StatusNotFound) 2942 return 2943 } 2944 header := "Bearer " + accessToken 2945 if auth := r.Header.Get("Authorization"); auth != header { 2946 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope) 2947 w.Header().Set("Www-Authenticate", challenge) 2948 w.WriteHeader(http.StatusUnauthorized) 2949 return 2950 } 2951 atomic.AddInt64(&successCount, 1) 2952 })) 2953 defer ts.Close() 2954 uri, err := url.Parse(ts.URL) 2955 if err != nil { 2956 t.Fatalf("invalid test http server: %v", err) 2957 } 2958 service = uri.Host 2959 2960 client := &Client{ 2961 Credential: func(ctx context.Context, reg string) (Credential, error) { 2962 if reg != uri.Host { 2963 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 2964 t.Error(err) 2965 return EmptyCredential, err 2966 } 2967 return Credential{ 2968 Username: username, 2969 Password: password, 2970 }, nil 2971 }, 2972 ForceAttemptOAuth2: true, 2973 Cache: NewCache(), 2974 } 2975 2976 // first request 2977 ctx := WithScopes(context.Background(), scopes...) 2978 req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 2979 if err != nil { 2980 t.Fatalf("failed to create test request: %v", err) 2981 } 2982 resp, err := client.Do(req) 2983 if err != nil { 2984 t.Fatalf("Client.Do() error = %v", err) 2985 } 2986 if resp.StatusCode != http.StatusOK { 2987 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 2988 } 2989 if wantRequestCount += 2; requestCount != wantRequestCount { 2990 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 2991 } 2992 if wantSuccessCount++; successCount != wantSuccessCount { 2993 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 2994 } 2995 if wantAuthCount++; authCount != wantAuthCount { 2996 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 2997 } 2998 2999 // repeated request 3000 // although the actual scope does not match the hinted scopes, the client 3001 // with cache cannot avoid a request to obtain a challenge but can prevent 3002 // a repeated call to the authorization server. 3003 req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil) 3004 if err != nil { 3005 t.Fatalf("failed to create test request: %v", err) 3006 } 3007 resp, err = client.Do(req) 3008 if err != nil { 3009 t.Fatalf("Client.Do() error = %v", err) 3010 } 3011 if resp.StatusCode != http.StatusOK { 3012 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 3013 } 3014 if wantRequestCount += 2; requestCount != wantRequestCount { 3015 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 3016 } 3017 if wantSuccessCount++; successCount != wantSuccessCount { 3018 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 3019 } 3020 if authCount != wantAuthCount { 3021 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 3022 } 3023 } 3024 3025 func TestClient_Do_Scope_Hint_Mismatch_PerHost(t *testing.T) { 3026 // set up server 1 3027 username1 := "test_user1" 3028 password1 := "test_password1" 3029 accessToken1 := "test/access/token/1" 3030 var requestCount1, wantRequestCount1 int64 3031 var successCount1, wantSuccessCount1 int64 3032 var authCount1, wantAuthCount1 int64 3033 var service1 string 3034 scopes1 := []string{ 3035 "repository:dst:pull,push", 3036 "repository:src:pull", 3037 } 3038 scope1 := "repository:test1:delete" 3039 as1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3040 if r.Method != http.MethodPost || r.URL.Path != "/" { 3041 t.Error("unexecuted attempt of authorization service") 3042 w.WriteHeader(http.StatusUnauthorized) 3043 return 3044 } 3045 if err := r.ParseForm(); err != nil { 3046 t.Errorf("failed to parse form: %v", err) 3047 w.WriteHeader(http.StatusUnauthorized) 3048 return 3049 } 3050 if got := r.PostForm.Get("grant_type"); got != "password" { 3051 t.Errorf("unexpected grant type: %v, want %v", got, "password") 3052 w.WriteHeader(http.StatusUnauthorized) 3053 return 3054 } 3055 if got := r.PostForm.Get("service"); got != service1 { 3056 t.Errorf("unexpected service: %v, want %v", got, service1) 3057 w.WriteHeader(http.StatusUnauthorized) 3058 return 3059 } 3060 if got := r.PostForm.Get("client_id"); got != defaultClientID { 3061 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 3062 w.WriteHeader(http.StatusUnauthorized) 3063 return 3064 } 3065 scopes := CleanScopes(append([]string{scope1}, scopes1...)) 3066 scope := strings.Join(scopes, " ") 3067 if got := r.PostForm.Get("scope"); got != scope { 3068 t.Errorf("unexpected scope: %v, want %v", got, scope) 3069 w.WriteHeader(http.StatusUnauthorized) 3070 return 3071 } 3072 if got := r.PostForm.Get("username"); got != username1 { 3073 t.Errorf("unexpected username: %v, want %v", got, username1) 3074 w.WriteHeader(http.StatusUnauthorized) 3075 return 3076 } 3077 if got := r.PostForm.Get("password"); got != password1 { 3078 t.Errorf("unexpected password: %v, want %v", got, password1) 3079 w.WriteHeader(http.StatusUnauthorized) 3080 return 3081 } 3082 3083 atomic.AddInt64(&authCount1, 1) 3084 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken1); err != nil { 3085 t.Errorf("failed to write %q: %v", r.URL, err) 3086 } 3087 })) 3088 defer as1.Close() 3089 ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3090 atomic.AddInt64(&requestCount1, 1) 3091 if r.Method != http.MethodGet || r.URL.Path != "/" { 3092 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 3093 w.WriteHeader(http.StatusNotFound) 3094 return 3095 } 3096 header := "Bearer " + accessToken1 3097 if auth := r.Header.Get("Authorization"); auth != header { 3098 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as1.URL, service1, scope1) 3099 w.Header().Set("Www-Authenticate", challenge) 3100 w.WriteHeader(http.StatusUnauthorized) 3101 return 3102 } 3103 atomic.AddInt64(&successCount1, 1) 3104 })) 3105 defer ts1.Close() 3106 uri1, err := url.Parse(ts1.URL) 3107 if err != nil { 3108 t.Fatalf("invalid test http server: %v", err) 3109 } 3110 service1 = uri1.Host 3111 client1 := &Client{ 3112 Credential: StaticCredential(uri1.Host, Credential{ 3113 Username: username1, 3114 Password: password1, 3115 }), 3116 ForceAttemptOAuth2: true, 3117 Cache: NewCache(), 3118 } 3119 3120 // set up server 1 3121 username2 := "test_user2" 3122 password2 := "test_password2" 3123 accessToken2 := "test/access/token/2" 3124 var requestCount2, wantRequestCount2 int64 3125 var successCount2, wantSuccessCount2 int64 3126 var authCount2, wantAuthCount2 int64 3127 var service2 string 3128 scopes2 := []string{ 3129 "repository:dst:pull,push", 3130 "repository:src:pull", 3131 } 3132 scope2 := "repository:test2:delete" 3133 as2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3134 if r.Method != http.MethodPost || r.URL.Path != "/" { 3135 t.Error("unexecuted attempt of authorization service") 3136 w.WriteHeader(http.StatusUnauthorized) 3137 return 3138 } 3139 if err := r.ParseForm(); err != nil { 3140 t.Errorf("failed to parse form: %v", err) 3141 w.WriteHeader(http.StatusUnauthorized) 3142 return 3143 } 3144 if got := r.PostForm.Get("grant_type"); got != "password" { 3145 t.Errorf("unexpected grant type: %v, want %v", got, "password") 3146 w.WriteHeader(http.StatusUnauthorized) 3147 return 3148 } 3149 if got := r.PostForm.Get("service"); got != service2 { 3150 t.Errorf("unexpected service: %v, want %v", got, service2) 3151 w.WriteHeader(http.StatusUnauthorized) 3152 return 3153 } 3154 if got := r.PostForm.Get("client_id"); got != defaultClientID { 3155 t.Errorf("unexpected client id: %v, want %v", got, defaultClientID) 3156 w.WriteHeader(http.StatusUnauthorized) 3157 return 3158 } 3159 scopes := CleanScopes(append([]string{scope2}, scopes2...)) 3160 scope := strings.Join(scopes, " ") 3161 if got := r.PostForm.Get("scope"); got != scope { 3162 t.Errorf("unexpected scope: %v, want %v", got, scope) 3163 w.WriteHeader(http.StatusUnauthorized) 3164 return 3165 } 3166 if got := r.PostForm.Get("username"); got != username2 { 3167 t.Errorf("unexpected username: %v, want %v", got, username2) 3168 w.WriteHeader(http.StatusUnauthorized) 3169 return 3170 } 3171 if got := r.PostForm.Get("password"); got != password2 { 3172 t.Errorf("unexpected password: %v, want %v", got, password2) 3173 w.WriteHeader(http.StatusUnauthorized) 3174 return 3175 } 3176 3177 atomic.AddInt64(&authCount2, 1) 3178 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken2); err != nil { 3179 t.Errorf("failed to write %q: %v", r.URL, err) 3180 } 3181 })) 3182 defer as2.Close() 3183 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3184 atomic.AddInt64(&requestCount2, 1) 3185 if r.Method != http.MethodGet || r.URL.Path != "/" { 3186 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 3187 w.WriteHeader(http.StatusNotFound) 3188 return 3189 } 3190 header := "Bearer " + accessToken2 3191 if auth := r.Header.Get("Authorization"); auth != header { 3192 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as2.URL, service2, scope2) 3193 w.Header().Set("Www-Authenticate", challenge) 3194 w.WriteHeader(http.StatusUnauthorized) 3195 return 3196 } 3197 atomic.AddInt64(&successCount2, 1) 3198 })) 3199 defer ts1.Close() 3200 uri2, err := url.Parse(ts2.URL) 3201 if err != nil { 3202 t.Fatalf("invalid test http server: %v", err) 3203 } 3204 service2 = uri2.Host 3205 client2 := &Client{ 3206 Credential: StaticCredential(uri2.Host, Credential{ 3207 Username: username2, 3208 Password: password2, 3209 }), 3210 ForceAttemptOAuth2: true, 3211 Cache: NewCache(), 3212 } 3213 3214 ctx := context.Background() 3215 ctx = WithScopesForHost(ctx, uri1.Host, scopes1...) 3216 ctx = WithScopesForHost(ctx, uri2.Host, scopes2...) 3217 // first request to server 1 3218 req1, err := http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 3219 if err != nil { 3220 t.Fatalf("failed to create test request: %v", err) 3221 } 3222 resp1, err := client1.Do(req1) 3223 if err != nil { 3224 t.Fatalf("Client.Do() error = %v", err) 3225 } 3226 if resp1.StatusCode != http.StatusOK { 3227 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 3228 } 3229 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 3230 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 3231 } 3232 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 3233 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 3234 } 3235 if wantAuthCount1++; authCount1 != wantAuthCount1 { 3236 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 3237 } 3238 3239 // first request to server 1 3240 req2, err := http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 3241 if err != nil { 3242 t.Fatalf("failed to create test request: %v", err) 3243 } 3244 resp2, err := client2.Do(req2) 3245 if err != nil { 3246 t.Fatalf("Client.Do() error = %v", err) 3247 } 3248 if resp2.StatusCode != http.StatusOK { 3249 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 3250 } 3251 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 3252 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 3253 } 3254 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 3255 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 3256 } 3257 if wantAuthCount2++; authCount2 != wantAuthCount2 { 3258 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 3259 } 3260 3261 // repeated request to server 1 3262 // although the actual scope does not match the hinted scopes, the client 3263 // with cache cannot avoid a request to obtain a challenge but can prevent 3264 // a repeated call to the authorization server. 3265 req1, err = http.NewRequestWithContext(ctx, http.MethodGet, ts1.URL, nil) 3266 if err != nil { 3267 t.Fatalf("failed to create test request: %v", err) 3268 } 3269 resp1, err = client1.Do(req1) 3270 if err != nil { 3271 t.Fatalf("Client.Do() error = %v", err) 3272 } 3273 if resp1.StatusCode != http.StatusOK { 3274 t.Errorf("Client.Do() = %v, want %v", resp1.StatusCode, http.StatusOK) 3275 } 3276 if wantRequestCount1 += 2; requestCount1 != wantRequestCount1 { 3277 t.Errorf("unexpected number of requests: %d, want %d", requestCount1, wantRequestCount1) 3278 } 3279 if wantSuccessCount1++; successCount1 != wantSuccessCount1 { 3280 t.Errorf("unexpected number of successful requests: %d, want %d", successCount1, wantSuccessCount1) 3281 } 3282 if authCount1 != wantAuthCount1 { 3283 t.Errorf("unexpected number of auth requests: %d, want %d", authCount1, wantAuthCount1) 3284 } 3285 3286 // repeated request to server 2 3287 // although the actual scope does not match the hinted scopes, the client 3288 // with cache cannot avoid a request to obtain a challenge but can prevent 3289 // a repeated call to the authorization server. 3290 req2, err = http.NewRequestWithContext(ctx, http.MethodGet, ts2.URL, nil) 3291 if err != nil { 3292 t.Fatalf("failed to create test request: %v", err) 3293 } 3294 resp2, err = client2.Do(req2) 3295 if err != nil { 3296 t.Fatalf("Client.Do() error = %v", err) 3297 } 3298 if resp2.StatusCode != http.StatusOK { 3299 t.Errorf("Client.Do() = %v, want %v", resp2.StatusCode, http.StatusOK) 3300 } 3301 if wantRequestCount2 += 2; requestCount2 != wantRequestCount2 { 3302 t.Errorf("unexpected number of requests: %d, want %d", requestCount2, wantRequestCount2) 3303 } 3304 if wantSuccessCount2++; successCount2 != wantSuccessCount2 { 3305 t.Errorf("unexpected number of successful requests: %d, want %d", successCount2, wantSuccessCount2) 3306 } 3307 if authCount2 != wantAuthCount2 { 3308 t.Errorf("unexpected number of auth requests: %d, want %d", authCount2, wantAuthCount2) 3309 } 3310 } 3311 3312 func TestClient_Do_Invalid_Credential_Basic(t *testing.T) { 3313 username := "test_user" 3314 password := "test_password" 3315 var requestCount, wantRequestCount int64 3316 var successCount, wantSuccessCount int64 3317 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3318 atomic.AddInt64(&requestCount, 1) 3319 if r.Method != http.MethodGet || r.URL.Path != "/" { 3320 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 3321 w.WriteHeader(http.StatusNotFound) 3322 return 3323 } 3324 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 3325 if auth := r.Header.Get("Authorization"); auth != header { 3326 w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`) 3327 w.WriteHeader(http.StatusUnauthorized) 3328 return 3329 } 3330 atomic.AddInt64(&successCount, 1) 3331 t.Error("authentication should fail but succeeded") 3332 })) 3333 defer ts.Close() 3334 uri, err := url.Parse(ts.URL) 3335 if err != nil { 3336 t.Fatalf("invalid test http server: %v", err) 3337 } 3338 3339 client := &Client{ 3340 Credential: func(ctx context.Context, reg string) (Credential, error) { 3341 if reg != uri.Host { 3342 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 3343 t.Error(err) 3344 return EmptyCredential, err 3345 } 3346 return Credential{ 3347 Username: username, 3348 Password: "bad credential", 3349 }, nil 3350 }, 3351 } 3352 3353 // request should fail 3354 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 3355 if err != nil { 3356 t.Fatalf("failed to create test request: %v", err) 3357 } 3358 resp, err := client.Do(req) 3359 if err != nil { 3360 t.Fatalf("Client.Do() error = %v", err) 3361 } 3362 if resp.StatusCode != http.StatusUnauthorized { 3363 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusUnauthorized) 3364 } 3365 if wantRequestCount += 2; requestCount != wantRequestCount { 3366 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 3367 } 3368 if successCount != wantSuccessCount { 3369 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 3370 } 3371 } 3372 3373 func TestClient_Do_Invalid_Credential_Bearer(t *testing.T) { 3374 username := "test_user" 3375 password := "test_password" 3376 accessToken := "test/access/token" 3377 var requestCount, wantRequestCount int64 3378 var successCount, wantSuccessCount int64 3379 var authCount, wantAuthCount int64 3380 var service string 3381 scopes := []string{ 3382 "repository:dst:pull,push", 3383 "repository:src:pull", 3384 } 3385 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3386 if r.Method != http.MethodGet || r.URL.Path != "/" { 3387 t.Error("unexecuted attempt of authorization service") 3388 w.WriteHeader(http.StatusUnauthorized) 3389 return 3390 } 3391 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 3392 if auth := r.Header.Get("Authorization"); auth != header { 3393 atomic.AddInt64(&authCount, 1) 3394 w.WriteHeader(http.StatusUnauthorized) 3395 return 3396 } 3397 t.Error("authentication should fail but succeeded") 3398 })) 3399 defer as.Close() 3400 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3401 atomic.AddInt64(&requestCount, 1) 3402 if r.Method != http.MethodGet || r.URL.Path != "/" { 3403 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 3404 w.WriteHeader(http.StatusNotFound) 3405 return 3406 } 3407 header := "Bearer " + accessToken 3408 if auth := r.Header.Get("Authorization"); auth != header { 3409 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " ")) 3410 w.Header().Set("Www-Authenticate", challenge) 3411 w.WriteHeader(http.StatusUnauthorized) 3412 return 3413 } 3414 atomic.AddInt64(&successCount, 1) 3415 t.Error("authentication should fail but succeeded") 3416 })) 3417 defer ts.Close() 3418 uri, err := url.Parse(ts.URL) 3419 if err != nil { 3420 t.Fatalf("invalid test http server: %v", err) 3421 } 3422 service = uri.Host 3423 3424 client := &Client{ 3425 Credential: func(ctx context.Context, reg string) (Credential, error) { 3426 if reg != uri.Host { 3427 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 3428 t.Error(err) 3429 return EmptyCredential, err 3430 } 3431 return Credential{ 3432 Username: username, 3433 Password: "bad credential", 3434 }, nil 3435 }, 3436 } 3437 3438 // request should fail 3439 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 3440 if err != nil { 3441 t.Fatalf("failed to create test request: %v", err) 3442 } 3443 _, err = client.Do(req) 3444 if err == nil { 3445 t.Fatalf("Client.Do() error = %v, wantErr %v", err, true) 3446 } 3447 if wantRequestCount++; requestCount != wantRequestCount { 3448 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 3449 } 3450 if successCount != wantSuccessCount { 3451 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 3452 } 3453 if wantAuthCount++; authCount != wantAuthCount { 3454 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 3455 } 3456 } 3457 3458 func TestClient_Do_Anonymous_Pull(t *testing.T) { 3459 accessToken := "test/access/token" 3460 var requestCount, wantRequestCount int64 3461 var successCount, wantSuccessCount int64 3462 var authCount, wantAuthCount int64 3463 var service string 3464 scope := "repository:test:pull" 3465 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3466 if r.Method != http.MethodGet || r.URL.Path != "/" { 3467 t.Error("unexecuted attempt of authorization service") 3468 w.WriteHeader(http.StatusUnauthorized) 3469 return 3470 } 3471 if auth := r.Header.Get("Authorization"); auth != "" { 3472 t.Errorf("unexpected auth: got %s, want %s", auth, "") 3473 w.WriteHeader(http.StatusUnauthorized) 3474 return 3475 } 3476 if got := r.URL.Query().Get("service"); got != service { 3477 t.Errorf("unexpected service: got %s, want %s", got, service) 3478 w.WriteHeader(http.StatusUnauthorized) 3479 return 3480 } 3481 if got := r.URL.Query().Get("scope"); got != scope { 3482 t.Errorf("unexpected scope: got %s, want %s", got, scope) 3483 w.WriteHeader(http.StatusUnauthorized) 3484 return 3485 } 3486 3487 atomic.AddInt64(&authCount, 1) 3488 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 3489 t.Errorf("failed to write %q: %v", r.URL, err) 3490 } 3491 })) 3492 defer as.Close() 3493 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3494 atomic.AddInt64(&requestCount, 1) 3495 if r.Method != http.MethodGet || r.URL.Path != "/" { 3496 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 3497 w.WriteHeader(http.StatusNotFound) 3498 return 3499 } 3500 header := "Bearer " + accessToken 3501 if auth := r.Header.Get("Authorization"); auth != header { 3502 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope) 3503 w.Header().Set("Www-Authenticate", challenge) 3504 w.WriteHeader(http.StatusUnauthorized) 3505 return 3506 } 3507 atomic.AddInt64(&successCount, 1) 3508 })) 3509 defer ts.Close() 3510 uri, err := url.Parse(ts.URL) 3511 if err != nil { 3512 t.Fatalf("invalid test http server: %v", err) 3513 } 3514 service = uri.Host 3515 3516 // request with the default client 3517 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 3518 if err != nil { 3519 t.Fatalf("failed to create test request: %v", err) 3520 } 3521 resp, err := DefaultClient.Do(req) 3522 if err != nil { 3523 t.Fatalf("Client.Do() error = %v", err) 3524 } 3525 if resp.StatusCode != http.StatusOK { 3526 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 3527 } 3528 if wantRequestCount += 2; requestCount != wantRequestCount { 3529 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 3530 } 3531 if wantSuccessCount++; successCount != wantSuccessCount { 3532 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 3533 } 3534 if wantAuthCount++; authCount != wantAuthCount { 3535 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 3536 } 3537 } 3538 3539 func TestClient_Do_Scheme_Change(t *testing.T) { 3540 username := "test_user" 3541 password := "test_password" 3542 accessToken := "test/access/token" 3543 var requestCount, wantRequestCount int64 3544 var successCount, wantSuccessCount int64 3545 var authCount, wantAuthCount int64 3546 var service string 3547 scope := "repository:test:pull" 3548 challengeBearerAuth := true 3549 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3550 if r.Method != http.MethodGet || r.URL.Path != "/" { 3551 t.Error("unexecuted attempt of authorization service") 3552 w.WriteHeader(http.StatusUnauthorized) 3553 return 3554 } 3555 header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 3556 if auth := r.Header.Get("Authorization"); auth != header { 3557 t.Errorf("unexpected auth: got %s, want %s", auth, header) 3558 w.WriteHeader(http.StatusUnauthorized) 3559 return 3560 } 3561 if got := r.URL.Query().Get("service"); got != service { 3562 t.Errorf("unexpected service: got %s, want %s", got, service) 3563 w.WriteHeader(http.StatusUnauthorized) 3564 return 3565 } 3566 if got := r.URL.Query().Get("scope"); got != scope { 3567 t.Errorf("unexpected scope: got %s, want %s", got, scope) 3568 w.WriteHeader(http.StatusUnauthorized) 3569 return 3570 } 3571 3572 atomic.AddInt64(&authCount, 1) 3573 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil { 3574 t.Errorf("failed to write %q: %v", r.URL, err) 3575 } 3576 })) 3577 defer as.Close() 3578 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3579 atomic.AddInt64(&requestCount, 1) 3580 if r.Method != http.MethodGet || r.URL.Path != "/" { 3581 t.Errorf("unexpected access: %s %s", r.Method, r.URL) 3582 w.WriteHeader(http.StatusNotFound) 3583 return 3584 } 3585 bearerHeader := "Bearer " + accessToken 3586 basicHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) 3587 header := r.Header.Get("Authorization") 3588 if (challengeBearerAuth && header != bearerHeader) || (!challengeBearerAuth && header != basicHeader) { 3589 var challenge string 3590 if challengeBearerAuth { 3591 challenge = fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope) 3592 } else { 3593 challenge = `Basic realm="Test Server"` 3594 } 3595 w.Header().Set("Www-Authenticate", challenge) 3596 w.WriteHeader(http.StatusUnauthorized) 3597 return 3598 } 3599 atomic.AddInt64(&successCount, 1) 3600 })) 3601 defer ts.Close() 3602 uri, err := url.Parse(ts.URL) 3603 if err != nil { 3604 t.Fatalf("invalid test http server: %v", err) 3605 } 3606 service = uri.Host 3607 3608 client := &Client{ 3609 Credential: func(ctx context.Context, reg string) (Credential, error) { 3610 if reg != uri.Host { 3611 err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host) 3612 t.Error(err) 3613 return EmptyCredential, err 3614 } 3615 return Credential{ 3616 Username: username, 3617 Password: password, 3618 }, nil 3619 }, 3620 Cache: NewCache(), 3621 } 3622 3623 // request with bearer auth 3624 req, err := http.NewRequest(http.MethodGet, ts.URL, nil) 3625 if err != nil { 3626 t.Fatalf("failed to create test request: %v", err) 3627 } 3628 resp, err := client.Do(req) 3629 if err != nil { 3630 t.Fatalf("Client.Do() error = %v", err) 3631 } 3632 if resp.StatusCode != http.StatusOK { 3633 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 3634 } 3635 if wantRequestCount += 2; requestCount != wantRequestCount { 3636 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 3637 } 3638 if wantSuccessCount++; successCount != wantSuccessCount { 3639 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 3640 } 3641 if wantAuthCount++; authCount != wantAuthCount { 3642 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 3643 } 3644 3645 // change to basic auth 3646 challengeBearerAuth = false 3647 req, err = http.NewRequest(http.MethodGet, ts.URL, nil) 3648 if err != nil { 3649 t.Fatalf("failed to create test request: %v", err) 3650 } 3651 resp, err = client.Do(req) 3652 if err != nil { 3653 t.Fatalf("Client.Do() error = %v", err) 3654 } 3655 if resp.StatusCode != http.StatusOK { 3656 t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK) 3657 } 3658 if wantRequestCount += 2; requestCount != wantRequestCount { 3659 t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount) 3660 } 3661 if wantSuccessCount++; successCount != wantSuccessCount { 3662 t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount) 3663 } 3664 if authCount != wantAuthCount { 3665 t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount) 3666 } 3667 } 3668 3669 func TestStaticCredential(t *testing.T) { 3670 tests := []struct { 3671 name string 3672 registry string 3673 target string 3674 cred Credential 3675 want Credential 3676 }{ 3677 { 3678 name: "Matched credential for regular registry", 3679 registry: "registry.example.com", 3680 target: "registry.example.com", 3681 cred: Credential{ 3682 Username: "username", 3683 Password: "password", 3684 }, 3685 want: Credential{ 3686 Username: "username", 3687 Password: "password", 3688 }, 3689 }, 3690 { 3691 name: "Matched credential for docker.io", 3692 registry: "docker.io", 3693 target: "registry-1.docker.io", 3694 cred: Credential{ 3695 Username: "username", 3696 Password: "password", 3697 }, 3698 want: Credential{ 3699 Username: "username", 3700 Password: "password", 3701 }, 3702 }, 3703 { 3704 name: "Mismatched credential for regular registry", 3705 registry: "registry.example.com", 3706 target: "whatever.example.com", 3707 cred: Credential{ 3708 Username: "username", 3709 Password: "password", 3710 }, 3711 want: EmptyCredential, 3712 }, 3713 { 3714 name: "Mismatched credential for docker.io", 3715 registry: "docker.io", 3716 target: "whatever.docker.io", 3717 cred: Credential{ 3718 Username: "username", 3719 Password: "password", 3720 }, 3721 want: EmptyCredential, 3722 }, 3723 } 3724 for _, tt := range tests { 3725 t.Run(tt.name, func(t *testing.T) { 3726 client := &Client{ 3727 Credential: StaticCredential(tt.registry, tt.cred), 3728 } 3729 ctx := context.Background() 3730 got, err := client.Credential(ctx, tt.target) 3731 if err != nil { 3732 t.Fatal("Client.Credential() error =", err) 3733 } 3734 if !reflect.DeepEqual(got, tt.want) { 3735 t.Errorf("Client.Credential() = %v, want %v", got, tt.want) 3736 } 3737 }) 3738 } 3739 } 3740 3741 func TestClient_StaticCredential_basicAuth(t *testing.T) { 3742 testUsername := "username" 3743 testPassword := "password" 3744 3745 // create a test server 3746 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3747 path := r.URL.Path 3748 if r.Method != http.MethodGet { 3749 w.WriteHeader(http.StatusNotFound) 3750 t.Fatal("unexpected access") 3751 } 3752 switch path { 3753 case "/basicAuth": 3754 wantedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(testUsername+":"+testPassword)) 3755 authHeader := r.Header.Get("Authorization") 3756 if authHeader != wantedAuthHeader { 3757 w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`) 3758 w.WriteHeader(http.StatusUnauthorized) 3759 } 3760 default: 3761 w.WriteHeader(http.StatusNotAcceptable) 3762 } 3763 })) 3764 defer ts.Close() 3765 host := ts.URL 3766 uri, _ := url.Parse(host) 3767 hostAddress := uri.Host 3768 basicAuthURL := fmt.Sprintf("%s/basicAuth", host) 3769 3770 // create a test client with the correct credentials 3771 clientValid := &Client{ 3772 Credential: StaticCredential(hostAddress, Credential{ 3773 Username: testUsername, 3774 Password: testPassword, 3775 }), 3776 } 3777 req, err := http.NewRequest(http.MethodGet, basicAuthURL, nil) 3778 if err != nil { 3779 t.Fatalf("could not create request, err = %v", err) 3780 } 3781 respValid, err := clientValid.Do(req) 3782 if err != nil { 3783 t.Fatalf("could not send request, err = %v", err) 3784 } 3785 if respValid.StatusCode != 200 { 3786 t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode) 3787 } 3788 3789 // create a test client with incorrect credentials 3790 clientInvalid := &Client{ 3791 Credential: StaticCredential(hostAddress, Credential{ 3792 Username: "foo", 3793 Password: "bar", 3794 }), 3795 } 3796 respInvalid, err := clientInvalid.Do(req) 3797 if err != nil { 3798 t.Fatalf("could not send request, err = %v", err) 3799 } 3800 if respInvalid.StatusCode != 401 { 3801 t.Errorf("incorrect status code: %d, expected 401", respInvalid.StatusCode) 3802 } 3803 } 3804 3805 func TestClient_StaticCredential_withAccessToken(t *testing.T) { 3806 var host string 3807 testAccessToken := "test/access/token" 3808 scope := "repository:test:pull,push" 3809 3810 // create an authorization server 3811 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3812 w.WriteHeader(http.StatusUnauthorized) 3813 t.Error("unexecuted attempt of authorization service") 3814 })) 3815 defer as.Close() 3816 3817 // create a test server 3818 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3819 path := r.URL.Path 3820 if r.Method != http.MethodGet { 3821 w.WriteHeader(http.StatusNotFound) 3822 t.Fatal("unexpected access") 3823 } 3824 switch path { 3825 case "/accessToken": 3826 wantedAuthHeader := "Bearer " + testAccessToken 3827 if auth := r.Header.Get("Authorization"); auth != wantedAuthHeader { 3828 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, host, scope) 3829 w.Header().Set("Www-Authenticate", challenge) 3830 w.WriteHeader(http.StatusUnauthorized) 3831 } 3832 default: 3833 w.WriteHeader(http.StatusNotAcceptable) 3834 } 3835 })) 3836 defer ts.Close() 3837 host = ts.URL 3838 uri, _ := url.Parse(host) 3839 hostAddress := uri.Host 3840 accessTokenURL := fmt.Sprintf("%s/accessToken", host) 3841 3842 // create a test client with the correct credentials 3843 clientValid := &Client{ 3844 Credential: StaticCredential(hostAddress, Credential{ 3845 AccessToken: testAccessToken, 3846 }), 3847 } 3848 req, err := http.NewRequest(http.MethodGet, accessTokenURL, nil) 3849 if err != nil { 3850 t.Fatalf("could not create request, err = %v", err) 3851 } 3852 respValid, err := clientValid.Do(req) 3853 if err != nil { 3854 t.Fatalf("could not send request, err = %v", err) 3855 } 3856 if respValid.StatusCode != 200 { 3857 t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode) 3858 } 3859 3860 // create a test client with incorrect credentials 3861 clientInvalid := &Client{ 3862 Credential: StaticCredential(hostAddress, Credential{ 3863 AccessToken: "foo", 3864 }), 3865 } 3866 respInvalid, err := clientInvalid.Do(req) 3867 if err != nil { 3868 t.Fatalf("could not send request, err = %v", err) 3869 } 3870 if respInvalid.StatusCode != 401 { 3871 t.Errorf("incorrect status code: %d, expected 401", respInvalid.StatusCode) 3872 } 3873 } 3874 3875 func TestClient_StaticCredential_withRefreshToken(t *testing.T) { 3876 var host string 3877 testAccessToken := "test/access/token" 3878 testRefreshToken := "test/refresh/token" 3879 scope := "repository:test:pull,push" 3880 3881 // create an authorization server 3882 as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3883 if r.Method != http.MethodGet && r.Method != http.MethodPost { 3884 w.WriteHeader(http.StatusUnauthorized) 3885 t.Error("unexecuted attempt of authorization service") 3886 } 3887 if err := r.ParseForm(); err != nil { 3888 w.WriteHeader(http.StatusUnauthorized) 3889 t.Error("failed to parse form") 3890 } 3891 if got := r.PostForm.Get("service"); got != host { 3892 w.WriteHeader(http.StatusUnauthorized) 3893 } 3894 // handles refresh token requests 3895 if got := r.PostForm.Get("grant_type"); got != "refresh_token" { 3896 w.WriteHeader(http.StatusUnauthorized) 3897 } 3898 if got := r.PostForm.Get("scope"); got != scope { 3899 w.WriteHeader(http.StatusUnauthorized) 3900 } 3901 if got := r.PostForm.Get("refresh_token"); got != testRefreshToken { 3902 w.WriteHeader(http.StatusUnauthorized) 3903 } 3904 // writes back access token 3905 if _, err := fmt.Fprintf(w, `{"access_token":%q}`, testAccessToken); err != nil { 3906 t.Fatalf("could not write back access token, error = %v", err) 3907 } 3908 })) 3909 defer as.Close() 3910 3911 // create a test server 3912 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3913 path := r.URL.Path 3914 if r.Method != http.MethodGet { 3915 w.WriteHeader(http.StatusNotFound) 3916 panic("unexpected access") 3917 } 3918 switch path { 3919 case "/refreshToken": 3920 wantedAuthHeader := "Bearer " + testAccessToken 3921 if auth := r.Header.Get("Authorization"); auth != wantedAuthHeader { 3922 challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, host, scope) 3923 w.Header().Set("Www-Authenticate", challenge) 3924 w.WriteHeader(http.StatusUnauthorized) 3925 } 3926 default: 3927 w.WriteHeader(http.StatusNotAcceptable) 3928 } 3929 })) 3930 defer ts.Close() 3931 host = ts.URL 3932 uri, _ := url.Parse(host) 3933 hostAddress := uri.Host 3934 refreshTokenURL := fmt.Sprintf("%s/refreshToken", host) 3935 3936 // create a test client with the correct credentials 3937 clientValid := &Client{ 3938 Credential: StaticCredential(hostAddress, Credential{ 3939 RefreshToken: testRefreshToken, 3940 }), 3941 } 3942 req, err := http.NewRequest(http.MethodGet, refreshTokenURL, nil) 3943 if err != nil { 3944 t.Fatalf("could not create request, err = %v", err) 3945 } 3946 respValid, err := clientValid.Do(req) 3947 if err != nil { 3948 t.Fatalf("could not send request, err = %v", err) 3949 } 3950 if respValid.StatusCode != 200 { 3951 t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode) 3952 } 3953 3954 // create a test client with incorrect credentials 3955 clientInvalid := &Client{ 3956 Credential: StaticCredential(hostAddress, Credential{ 3957 RefreshToken: "bar", 3958 }), 3959 } 3960 _, err = clientInvalid.Do(req) 3961 3962 var expectedError *errcode.ErrorResponse 3963 if !errors.As(err, &expectedError) || expectedError.StatusCode != http.StatusUnauthorized { 3964 t.Errorf("incorrect error: %v, expected %v", err, expectedError) 3965 } 3966 } 3967 3968 func TestClient_fetchBasicAuth(t *testing.T) { 3969 c := &Client{ 3970 Credential: func(ctx context.Context, registry string) (Credential, error) { 3971 return EmptyCredential, nil 3972 }, 3973 } 3974 _, err := c.fetchBasicAuth(context.Background(), "") 3975 if err != ErrBasicCredentialNotFound { 3976 t.Errorf("incorrect error: %v, expected %v", err, ErrBasicCredentialNotFound) 3977 } 3978 }