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