github.com/dtroyer-salad/og2/v2@v2.0.0-20240412154159-c47231610877/registry/remote/credentials/store_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 credentials 17 18 import ( 19 "context" 20 "encoding/json" 21 "errors" 22 "os" 23 "path/filepath" 24 "reflect" 25 "testing" 26 27 "oras.land/oras-go/v2/registry/remote/auth" 28 "oras.land/oras-go/v2/registry/remote/credentials/internal/config/configtest" 29 ) 30 31 type badStore struct{} 32 33 var errBadStore = errors.New("bad store!") 34 35 // Get retrieves credentials from the store for the given server address. 36 func (s *badStore) Get(ctx context.Context, serverAddress string) (auth.Credential, error) { 37 return auth.EmptyCredential, errBadStore 38 } 39 40 // Put saves credentials into the store for the given server address. 41 func (s *badStore) Put(ctx context.Context, serverAddress string, cred auth.Credential) error { 42 return errBadStore 43 } 44 45 // Delete removes credentials from the store for the given server address. 46 func (s *badStore) Delete(ctx context.Context, serverAddress string) error { 47 return errBadStore 48 } 49 50 func Test_DynamicStore_IsAuthConfigured(t *testing.T) { 51 tempDir := t.TempDir() 52 53 tests := []struct { 54 name string 55 fileName string 56 shouldCreateFile bool 57 cfg configtest.Config 58 want bool 59 }{ 60 { 61 name: "not existing file", 62 fileName: "config.json", 63 shouldCreateFile: false, 64 cfg: configtest.Config{}, 65 want: false, 66 }, 67 { 68 name: "no auth", 69 fileName: "config.json", 70 shouldCreateFile: true, 71 cfg: configtest.Config{ 72 SomeConfigField: 123, 73 }, 74 want: false, 75 }, 76 { 77 name: "empty auths exist", 78 fileName: "empty_auths.json", 79 shouldCreateFile: true, 80 cfg: configtest.Config{ 81 AuthConfigs: map[string]configtest.AuthConfig{}, 82 }, 83 want: false, 84 }, 85 { 86 name: "auths exist, but no credential", 87 fileName: "no_cred_auths.json", 88 shouldCreateFile: true, 89 cfg: configtest.Config{ 90 AuthConfigs: map[string]configtest.AuthConfig{ 91 "test.example.com": {}, 92 }, 93 }, 94 want: true, 95 }, 96 { 97 name: "auths exist", 98 fileName: "auths.json", 99 shouldCreateFile: true, 100 cfg: configtest.Config{ 101 AuthConfigs: map[string]configtest.AuthConfig{ 102 "test.example.com": { 103 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 104 }, 105 }, 106 }, 107 want: true, 108 }, 109 { 110 name: "credsStore exists", 111 fileName: "credsStore.json", 112 shouldCreateFile: true, 113 cfg: configtest.Config{ 114 CredentialsStore: "teststore", 115 }, 116 want: true, 117 }, 118 { 119 name: "empty credHelpers exist", 120 fileName: "empty_credsStore.json", 121 shouldCreateFile: true, 122 cfg: configtest.Config{ 123 CredentialHelpers: map[string]string{}, 124 }, 125 want: false, 126 }, 127 { 128 name: "credHelpers exist", 129 fileName: "credsStore.json", 130 shouldCreateFile: true, 131 cfg: configtest.Config{ 132 CredentialHelpers: map[string]string{ 133 "test.example.com": "testhelper", 134 }, 135 }, 136 want: true, 137 }, 138 { 139 name: "all exist", 140 fileName: "credsStore.json", 141 shouldCreateFile: true, 142 cfg: configtest.Config{ 143 SomeConfigField: 123, 144 AuthConfigs: map[string]configtest.AuthConfig{ 145 "test.example.com": {}, 146 }, 147 CredentialsStore: "teststore", 148 CredentialHelpers: map[string]string{ 149 "test.example.com": "testhelper", 150 }, 151 }, 152 want: true, 153 }, 154 } 155 for _, tt := range tests { 156 t.Run(tt.name, func(t *testing.T) { 157 // prepare test content 158 configPath := filepath.Join(tempDir, tt.fileName) 159 if tt.shouldCreateFile { 160 jsonStr, err := json.Marshal(tt.cfg) 161 if err != nil { 162 t.Fatalf("failed to marshal config: %v", err) 163 } 164 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 165 t.Fatalf("failed to write config file: %v", err) 166 } 167 } 168 169 ds, err := NewStore(configPath, StoreOptions{}) 170 if err != nil { 171 t.Fatal("newStore() error =", err) 172 } 173 if got := ds.IsAuthConfigured(); got != tt.want { 174 t.Errorf("DynamicStore.IsAuthConfigured() = %v, want %v", got, tt.want) 175 } 176 }) 177 } 178 } 179 180 func Test_DynamicStore_authConfigured(t *testing.T) { 181 // prepare test content 182 tempDir := t.TempDir() 183 configPath := filepath.Join(tempDir, "auth_configured.json") 184 config := configtest.Config{ 185 AuthConfigs: map[string]configtest.AuthConfig{ 186 "xxx": {}, 187 }, 188 SomeConfigField: 123, 189 } 190 jsonStr, err := json.Marshal(config) 191 if err != nil { 192 t.Fatalf("failed to marshal config: %v", err) 193 } 194 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 195 t.Fatalf("failed to write config file: %v", err) 196 } 197 198 ds, err := NewStore(configPath, StoreOptions{AllowPlaintextPut: true}) 199 if err != nil { 200 t.Fatal("NewStore() error =", err) 201 } 202 203 // test IsAuthConfigured 204 authConfigured := ds.IsAuthConfigured() 205 if want := true; authConfigured != want { 206 t.Errorf("DynamicStore.IsAuthConfigured() = %v, want %v", authConfigured, want) 207 } 208 209 serverAddr := "test.example.com" 210 cred := auth.Credential{ 211 Username: "username", 212 Password: "password", 213 } 214 ctx := context.Background() 215 216 // test put 217 if err := ds.Put(ctx, serverAddr, cred); err != nil { 218 t.Fatal("DynamicStore.Get() error =", err) 219 } 220 // Put() should not set detected store back to config 221 if got := ds.detectedCredsStore; got != "" { 222 t.Errorf("ds.detectedCredsStore = %v, want empty", got) 223 } 224 if got := ds.config.CredentialsStore(); got != "" { 225 t.Errorf("ds.config.CredentialsStore() = %v, want empty", got) 226 } 227 228 // test get 229 got, err := ds.Get(ctx, serverAddr) 230 if err != nil { 231 t.Fatal("DynamicStore.Get() error =", err) 232 } 233 if want := cred; got != want { 234 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 235 } 236 237 // test delete 238 err = ds.Delete(ctx, serverAddr) 239 if err != nil { 240 t.Fatal("DynamicStore.Delete() error =", err) 241 } 242 243 // verify delete 244 got, err = ds.Get(ctx, serverAddr) 245 if err != nil { 246 t.Fatal("DynamicStore.Get() error =", err) 247 } 248 if want := auth.EmptyCredential; got != want { 249 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 250 } 251 } 252 253 func Test_DynamicStore_authConfigured_DetectDefaultNativeStore(t *testing.T) { 254 // prepare test content 255 tempDir := t.TempDir() 256 configPath := filepath.Join(tempDir, "auth_configured.json") 257 config := configtest.Config{ 258 AuthConfigs: map[string]configtest.AuthConfig{ 259 "xxx": {}, 260 }, 261 SomeConfigField: 123, 262 } 263 jsonStr, err := json.Marshal(config) 264 if err != nil { 265 t.Fatalf("failed to marshal config: %v", err) 266 } 267 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 268 t.Fatalf("failed to write config file: %v", err) 269 } 270 271 opts := StoreOptions{ 272 AllowPlaintextPut: true, 273 DetectDefaultNativeStore: true, 274 } 275 ds, err := NewStore(configPath, opts) 276 if err != nil { 277 t.Fatal("NewStore() error =", err) 278 } 279 280 // test IsAuthConfigured 281 authConfigured := ds.IsAuthConfigured() 282 if want := true; authConfigured != want { 283 t.Errorf("DynamicStore.IsAuthConfigured() = %v, want %v", authConfigured, want) 284 } 285 286 serverAddr := "test.example.com" 287 cred := auth.Credential{ 288 Username: "username", 289 Password: "password", 290 } 291 ctx := context.Background() 292 293 // test put 294 if err := ds.Put(ctx, serverAddr, cred); err != nil { 295 t.Fatal("DynamicStore.Get() error =", err) 296 } 297 // Put() should not set detected store back to config 298 if got := ds.detectedCredsStore; got != "" { 299 t.Errorf("ds.detectedCredsStore = %v, want empty", got) 300 } 301 if got := ds.config.CredentialsStore(); got != "" { 302 t.Errorf("ds.config.CredentialsStore() = %v, want empty", got) 303 } 304 305 // test get 306 got, err := ds.Get(ctx, serverAddr) 307 if err != nil { 308 t.Fatal("DynamicStore.Get() error =", err) 309 } 310 if want := cred; got != want { 311 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 312 } 313 314 // test delete 315 err = ds.Delete(ctx, serverAddr) 316 if err != nil { 317 t.Fatal("DynamicStore.Delete() error =", err) 318 } 319 320 // verify delete 321 got, err = ds.Get(ctx, serverAddr) 322 if err != nil { 323 t.Fatal("DynamicStore.Get() error =", err) 324 } 325 if want := auth.EmptyCredential; got != want { 326 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 327 } 328 } 329 330 func Test_DynamicStore_noAuthConfigured(t *testing.T) { 331 // prepare test content 332 tempDir := t.TempDir() 333 configPath := filepath.Join(tempDir, "no_auth_configured.json") 334 cfg := configtest.Config{ 335 SomeConfigField: 123, 336 } 337 jsonStr, err := json.Marshal(cfg) 338 if err != nil { 339 t.Fatalf("failed to marshal config: %v", err) 340 } 341 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 342 t.Fatalf("failed to write config file: %v", err) 343 } 344 345 ds, err := NewStore(configPath, StoreOptions{AllowPlaintextPut: true}) 346 if err != nil { 347 t.Fatal("NewStore() error =", err) 348 } 349 350 // test IsAuthConfigured 351 authConfigured := ds.IsAuthConfigured() 352 if want := false; authConfigured != want { 353 t.Errorf("DynamicStore.IsAuthConfigured() = %v, want %v", authConfigured, want) 354 } 355 356 serverAddr := "test.example.com" 357 cred := auth.Credential{ 358 Username: "username", 359 Password: "password", 360 } 361 ctx := context.Background() 362 363 // Get() should not set detected store back to config 364 if _, err := ds.Get(ctx, serverAddr); err != nil { 365 t.Fatal("DynamicStore.Get() error =", err) 366 } 367 368 // test put 369 if err := ds.Put(ctx, serverAddr, cred); err != nil { 370 t.Fatal("DynamicStore.Put() error =", err) 371 } 372 // Put() should not set detected store back to config 373 if got := ds.detectedCredsStore; got != "" { 374 t.Errorf("ds.detectedCredsStore = %v, want empty", got) 375 } 376 if got := ds.config.CredentialsStore(); got != "" { 377 t.Errorf("ds.config.CredentialsStore() = %v, want empty", got) 378 } 379 380 // test get 381 got, err := ds.Get(ctx, serverAddr) 382 if err != nil { 383 t.Fatal("DynamicStore.Get() error =", err) 384 } 385 if want := cred; got != want { 386 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 387 } 388 389 // test delete 390 err = ds.Delete(ctx, serverAddr) 391 if err != nil { 392 t.Fatal("DynamicStore.Delete() error =", err) 393 } 394 395 // verify delete 396 got, err = ds.Get(ctx, serverAddr) 397 if err != nil { 398 t.Fatal("DynamicStore.Get() error =", err) 399 } 400 if want := auth.EmptyCredential; got != want { 401 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 402 } 403 } 404 405 func Test_DynamicStore_noAuthConfigured_DetectDefaultNativeStore(t *testing.T) { 406 // prepare test content 407 tempDir := t.TempDir() 408 configPath := filepath.Join(tempDir, "no_auth_configured.json") 409 cfg := configtest.Config{ 410 SomeConfigField: 123, 411 } 412 jsonStr, err := json.Marshal(cfg) 413 if err != nil { 414 t.Fatalf("failed to marshal config: %v", err) 415 } 416 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 417 t.Fatalf("failed to write config file: %v", err) 418 } 419 420 opts := StoreOptions{ 421 AllowPlaintextPut: true, 422 DetectDefaultNativeStore: true, 423 } 424 ds, err := NewStore(configPath, opts) 425 if err != nil { 426 t.Fatal("NewStore() error =", err) 427 } 428 429 // test IsAuthConfigured 430 authConfigured := ds.IsAuthConfigured() 431 if want := false; authConfigured != want { 432 t.Errorf("DynamicStore.IsAuthConfigured() = %v, want %v", authConfigured, want) 433 } 434 435 serverAddr := "test.example.com" 436 cred := auth.Credential{ 437 Username: "username", 438 Password: "password", 439 } 440 ctx := context.Background() 441 442 // Get() should set detectedCredsStore only, but should not save it back to config 443 if _, err := ds.Get(ctx, serverAddr); err != nil { 444 t.Fatal("DynamicStore.Get() error =", err) 445 } 446 if defaultStore := getDefaultHelperSuffix(); defaultStore != "" { 447 if got := ds.detectedCredsStore; got != defaultStore { 448 t.Errorf("ds.detectedCredsStore = %v, want %v", got, defaultStore) 449 } 450 } 451 if got := ds.config.CredentialsStore(); got != "" { 452 t.Errorf("ds.config.CredentialsStore() = %v, want empty", got) 453 } 454 455 // test put 456 if err := ds.Put(ctx, serverAddr, cred); err != nil { 457 t.Fatal("DynamicStore.Put() error =", err) 458 } 459 460 // Put() should set the detected store back to config 461 if got := ds.config.CredentialsStore(); got != ds.detectedCredsStore { 462 t.Errorf("ds.config.CredentialsStore() = %v, want %v", got, ds.detectedCredsStore) 463 } 464 465 // test get 466 got, err := ds.Get(ctx, serverAddr) 467 if err != nil { 468 t.Fatal("DynamicStore.Get() error =", err) 469 } 470 if want := cred; got != want { 471 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 472 } 473 474 // test delete 475 err = ds.Delete(ctx, serverAddr) 476 if err != nil { 477 t.Fatal("DynamicStore.Delete() error =", err) 478 } 479 480 // verify delete 481 got, err = ds.Get(ctx, serverAddr) 482 if err != nil { 483 t.Fatal("DynamicStore.Get() error =", err) 484 } 485 if want := auth.EmptyCredential; got != want { 486 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 487 } 488 } 489 490 func Test_DynamicStore_fileStore_AllowPlainTextPut(t *testing.T) { 491 // prepare test content 492 tempDir := t.TempDir() 493 configPath := filepath.Join(tempDir, "config.json") 494 serverAddr := "newtest.example.com" 495 cred := auth.Credential{ 496 Username: "username", 497 Password: "password", 498 } 499 ctx := context.Background() 500 501 cfg := configtest.Config{ 502 AuthConfigs: map[string]configtest.AuthConfig{ 503 "test.example.com": {}, 504 }, 505 SomeConfigField: 123, 506 } 507 jsonStr, err := json.Marshal(cfg) 508 if err != nil { 509 t.Fatalf("failed to marshal config: %v", err) 510 } 511 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 512 t.Fatalf("failed to write config file: %v", err) 513 } 514 515 // test default option 516 ds, err := NewStore(configPath, StoreOptions{}) 517 if err != nil { 518 t.Fatal("NewStore() error =", err) 519 } 520 err = ds.Put(ctx, serverAddr, cred) 521 if wantErr := ErrPlaintextPutDisabled; !errors.Is(err, wantErr) { 522 t.Errorf("DynamicStore.Put() error = %v, wantErr %v", err, wantErr) 523 } 524 525 // test AllowPlainTextPut = true 526 ds, err = NewStore(configPath, StoreOptions{AllowPlaintextPut: true}) 527 if err != nil { 528 t.Fatal("NewStore() error =", err) 529 } 530 if err := ds.Put(ctx, serverAddr, cred); err != nil { 531 t.Error("DynamicStore.Put() error =", err) 532 } 533 534 // verify config file 535 configFile, err := os.Open(configPath) 536 if err != nil { 537 t.Fatalf("failed to open config file: %v", err) 538 } 539 defer configFile.Close() 540 var gotCfg configtest.Config 541 if err := json.NewDecoder(configFile).Decode(&gotCfg); err != nil { 542 t.Fatalf("failed to decode config file: %v", err) 543 } 544 wantCfg := configtest.Config{ 545 AuthConfigs: map[string]configtest.AuthConfig{ 546 "test.example.com": {}, 547 serverAddr: { 548 Auth: "dXNlcm5hbWU6cGFzc3dvcmQ=", 549 }, 550 }, 551 SomeConfigField: cfg.SomeConfigField, 552 } 553 if !reflect.DeepEqual(gotCfg, wantCfg) { 554 t.Errorf("Decoded config = %v, want %v", gotCfg, wantCfg) 555 } 556 } 557 558 func Test_DynamicStore_getHelperSuffix(t *testing.T) { 559 tests := []struct { 560 name string 561 configPath string 562 serverAddress string 563 want string 564 }{ 565 { 566 name: "Get cred helper: registry_helper1", 567 configPath: "testdata/credHelpers_config.json", 568 serverAddress: "registry1.example.com", 569 want: "registry1-helper", 570 }, 571 { 572 name: "Get cred helper: registry_helper2", 573 configPath: "testdata/credHelpers_config.json", 574 serverAddress: "registry2.example.com", 575 want: "registry2-helper", 576 }, 577 { 578 name: "Empty cred helper configured", 579 configPath: "testdata/credHelpers_config.json", 580 serverAddress: "registry3.example.com", 581 want: "", 582 }, 583 { 584 name: "No cred helper and creds store configured", 585 configPath: "testdata/credHelpers_config.json", 586 serverAddress: "whatever.example.com", 587 want: "", 588 }, 589 { 590 name: "Choose cred helper over creds store", 591 configPath: "testdata/credsStore_config.json", 592 serverAddress: "test.example.com", 593 want: "test-helper", 594 }, 595 { 596 name: "No cred helper configured, choose cred store", 597 configPath: "testdata/credsStore_config.json", 598 serverAddress: "whatever.example.com", 599 want: "teststore", 600 }, 601 } 602 for _, tt := range tests { 603 t.Run(tt.name, func(t *testing.T) { 604 ds, err := NewStore(tt.configPath, StoreOptions{}) 605 if err != nil { 606 t.Fatal("NewStore() error =", err) 607 } 608 if got := ds.getHelperSuffix(tt.serverAddress); got != tt.want { 609 t.Errorf("DynamicStore.getHelperSuffix() = %v, want %v", got, tt.want) 610 } 611 }) 612 } 613 } 614 615 func Test_DynamicStore_ConfigPath(t *testing.T) { 616 path := "../../testdata/credsStore_config.json" 617 var err error 618 store, err := NewStore(path, StoreOptions{}) 619 if err != nil { 620 t.Fatal("NewFileStore() error =", err) 621 } 622 got := store.ConfigPath() 623 if got != path { 624 t.Errorf("Config.GetPath() = %v, want %v", got, path) 625 } 626 } 627 628 func Test_DynamicStore_getStore_nativeStore(t *testing.T) { 629 tests := []struct { 630 name string 631 configPath string 632 serverAddress string 633 }{ 634 { 635 name: "Cred helper configured for registry1.example.com", 636 configPath: "testdata/credHelpers_config.json", 637 serverAddress: "registry1.example.com", 638 }, 639 { 640 name: "Cred helper configured for registry2.example.com", 641 configPath: "testdata/credHelpers_config.json", 642 serverAddress: "registry2.example.com", 643 }, 644 { 645 name: "Cred helper configured for test.example.com", 646 configPath: "testdata/credsStore_config.json", 647 serverAddress: "test.example.com", 648 }, 649 { 650 name: "No cred helper configured, use creds store", 651 configPath: "testdata/credsStore_config.json", 652 serverAddress: "whaterver.example.com", 653 }, 654 } 655 for _, tt := range tests { 656 t.Run(tt.name, func(t *testing.T) { 657 ds, err := NewStore(tt.configPath, StoreOptions{}) 658 if err != nil { 659 t.Fatal("NewStore() error =", err) 660 } 661 gotStore := ds.getStore(tt.serverAddress) 662 if _, ok := gotStore.(*nativeStore); !ok { 663 t.Errorf("gotStore is not a native store") 664 } 665 }) 666 } 667 } 668 669 func Test_DynamicStore_getStore_fileStore(t *testing.T) { 670 tests := []struct { 671 name string 672 configPath string 673 serverAddress string 674 }{ 675 { 676 name: "Empty cred helper configured for registry3.example.com", 677 configPath: "testdata/credHelpers_config.json", 678 serverAddress: "registry3.example.com", 679 }, 680 { 681 name: "No cred helper configured", 682 configPath: "testdata/credHelpers_config.json", 683 serverAddress: "whatever.example.com", 684 }, 685 } 686 for _, tt := range tests { 687 t.Run(tt.name, func(t *testing.T) { 688 ds, err := NewStore(tt.configPath, StoreOptions{}) 689 if err != nil { 690 t.Fatal("NewStore() error =", err) 691 } 692 gotStore := ds.getStore(tt.serverAddress) 693 gotFS1, ok := gotStore.(*FileStore) 694 if !ok { 695 t.Errorf("gotStore is not a file store") 696 } 697 698 // get again, the two file stores should be based on the same config instance 699 gotStore = ds.getStore(tt.serverAddress) 700 gotFS2, ok := gotStore.(*FileStore) 701 if !ok { 702 t.Errorf("gotStore is not a file store") 703 } 704 if gotFS1.config != gotFS2.config { 705 t.Errorf("gotFS1 and gotFS2 are not based on the same config") 706 } 707 }) 708 } 709 } 710 711 func Test_storeWithFallbacks_Get(t *testing.T) { 712 // prepare test content 713 server1 := "foo.registry.com" 714 cred1 := auth.Credential{ 715 Username: "username", 716 Password: "password", 717 } 718 server2 := "bar.registry.com" 719 cred2 := auth.Credential{ 720 RefreshToken: "identity_token", 721 } 722 723 primaryStore := &testStore{} 724 fallbackStore1 := &testStore{ 725 storage: map[string]auth.Credential{ 726 server1: cred1, 727 }, 728 } 729 fallbackStore2 := &testStore{ 730 storage: map[string]auth.Credential{ 731 server2: cred2, 732 }, 733 } 734 sf := NewStoreWithFallbacks(primaryStore, fallbackStore1, fallbackStore2) 735 ctx := context.Background() 736 737 // test Get() 738 got1, err := sf.Get(ctx, server1) 739 if err != nil { 740 t.Fatalf("storeWithFallbacks.Get(%s) error = %v", server1, err) 741 } 742 if want := cred1; got1 != cred1 { 743 t.Errorf("storeWithFallbacks.Get(%s) = %v, want %v", server1, got1, want) 744 } 745 got2, err := sf.Get(ctx, server2) 746 if err != nil { 747 t.Fatalf("storeWithFallbacks.Get(%s) error = %v", server2, err) 748 } 749 if want := cred2; got2 != cred2 { 750 t.Errorf("storeWithFallbacks.Get(%s) = %v, want %v", server2, got2, want) 751 } 752 753 // test Get(): no credential found 754 got, err := sf.Get(ctx, "whaterver") 755 if err != nil { 756 t.Fatal("storeWithFallbacks.Get() error =", err) 757 } 758 if want := auth.EmptyCredential; got != want { 759 t.Errorf("storeWithFallbacks.Get() = %v, want %v", got, want) 760 } 761 } 762 763 func Test_storeWithFallbacks_Get_throwError(t *testing.T) { 764 badStore := &badStore{} 765 goodStore := &testStore{} 766 sf := NewStoreWithFallbacks(badStore, goodStore) 767 ctx := context.Background() 768 769 // test Get(): should throw error 770 _, err := sf.Get(ctx, "whatever") 771 if wantErr := errBadStore; !errors.Is(err, wantErr) { 772 t.Errorf("storeWithFallback.Get() error = %v, wantErr %v", err, wantErr) 773 } 774 } 775 776 func Test_storeWithFallbacks_Put(t *testing.T) { 777 // prepare test content 778 cfg := configtest.Config{ 779 SomeConfigField: 123, 780 } 781 jsonStr, err := json.Marshal(cfg) 782 if err != nil { 783 t.Fatalf("failed to marshal config: %v", err) 784 } 785 tempDir := t.TempDir() 786 configPath := filepath.Join(tempDir, "no_auth_configured.json") 787 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 788 t.Fatalf("failed to write config file: %v", err) 789 } 790 opts := StoreOptions{ 791 AllowPlaintextPut: true, 792 } 793 primaryStore, err := NewStore(configPath, opts) // plaintext enabled 794 if err != nil { 795 t.Fatalf("NewStore(%s) error = %v", configPath, err) 796 } 797 badStore := &badStore{} // bad store 798 sf := NewStoreWithFallbacks(primaryStore, badStore) 799 ctx := context.Background() 800 801 server := "example.registry.com" 802 cred := auth.Credential{ 803 Username: "username", 804 Password: "password", 805 } 806 // test Put() 807 if err := sf.Put(ctx, server, cred); err != nil { 808 t.Fatal("storeWithFallbacks.Put() error =", err) 809 } 810 // verify Get() 811 got, err := sf.Get(ctx, server) 812 if err != nil { 813 t.Fatal("storeWithFallbacks.Get() error =", err) 814 } 815 if want := cred; got != want { 816 t.Errorf("storeWithFallbacks.Get() = %v, want %v", got, want) 817 } 818 } 819 820 func Test_storeWithFallbacks_Put_throwError(t *testing.T) { 821 badStore := &badStore{} 822 goodStore := &testStore{} 823 sf := NewStoreWithFallbacks(badStore, goodStore) 824 ctx := context.Background() 825 826 // test Put(): should thrown error 827 err := sf.Put(ctx, "whatever", auth.Credential{}) 828 if wantErr := errBadStore; !errors.Is(err, wantErr) { 829 t.Errorf("storeWithFallback.Put() error = %v, wantErr %v", err, wantErr) 830 } 831 } 832 833 func Test_storeWithFallbacks_Delete(t *testing.T) { 834 // prepare test content 835 server1 := "foo.registry.com" 836 cred1 := auth.Credential{ 837 Username: "username", 838 Password: "password", 839 } 840 server2 := "bar.registry.com" 841 cred2 := auth.Credential{ 842 RefreshToken: "identity_token", 843 } 844 845 primaryStore := &testStore{ 846 storage: map[string]auth.Credential{ 847 server1: cred1, 848 server2: cred2, 849 }, 850 } 851 badStore := &badStore{} 852 sf := NewStoreWithFallbacks(primaryStore, badStore) 853 ctx := context.Background() 854 855 // test Delete(): server1 856 if err := sf.Delete(ctx, server1); err != nil { 857 t.Fatal("storeWithFallback.Delete()") 858 } 859 // verify primary store 860 if want := map[string]auth.Credential{server2: cred2}; !reflect.DeepEqual(primaryStore.storage, want) { 861 t.Errorf("primaryStore.storage = %v, want %v", primaryStore.storage, want) 862 } 863 864 // test Delete(): server2 865 if err := sf.Delete(ctx, server2); err != nil { 866 t.Fatal("storeWithFallback.Delete()") 867 } 868 // verify primary store 869 if want := map[string]auth.Credential{}; !reflect.DeepEqual(primaryStore.storage, want) { 870 t.Errorf("primaryStore.storage = %v, want %v", primaryStore.storage, want) 871 } 872 } 873 874 func Test_storeWithFallbacks_Delete_throwError(t *testing.T) { 875 badStore := &badStore{} 876 goodStore := &testStore{} 877 sf := NewStoreWithFallbacks(badStore, goodStore) 878 ctx := context.Background() 879 880 // test Delete(): should throw error 881 err := sf.Delete(ctx, "whatever") 882 if wantErr := errBadStore; !errors.Is(err, wantErr) { 883 t.Errorf("storeWithFallback.Delete() error = %v, wantErr %v", err, wantErr) 884 } 885 } 886 887 func Test_getDockerConfigPath_env(t *testing.T) { 888 dir, err := os.Getwd() 889 if err != nil { 890 t.Fatal("os.Getwd() error =", err) 891 } 892 t.Setenv("DOCKER_CONFIG", dir) 893 894 got, err := getDockerConfigPath() 895 if err != nil { 896 t.Fatal("getDockerConfigPath() error =", err) 897 } 898 if want := filepath.Join(dir, "config.json"); got != want { 899 t.Errorf("getDockerConfigPath() = %v, want %v", got, want) 900 } 901 } 902 903 func Test_getDockerConfigPath_homeDir(t *testing.T) { 904 t.Setenv("DOCKER_CONFIG", "") 905 906 got, err := getDockerConfigPath() 907 if err != nil { 908 t.Fatal("getDockerConfigPath() error =", err) 909 } 910 homeDir, err := os.UserHomeDir() 911 if err != nil { 912 t.Fatal("os.UserHomeDir()") 913 } 914 if want := filepath.Join(homeDir, ".docker", "config.json"); got != want { 915 t.Errorf("getDockerConfigPath() = %v, want %v", got, want) 916 } 917 } 918 919 func TestNewStoreFromDocker(t *testing.T) { 920 // prepare test content 921 tempDir := t.TempDir() 922 configPath := filepath.Join(tempDir, "config.json") 923 t.Setenv("DOCKER_CONFIG", tempDir) 924 925 serverAddr1 := "test.example.com" 926 cred1 := auth.Credential{ 927 Username: "foo", 928 Password: "bar", 929 } 930 config := configtest.Config{ 931 AuthConfigs: map[string]configtest.AuthConfig{ 932 serverAddr1: { 933 Auth: "Zm9vOmJhcg==", 934 }, 935 }, 936 SomeConfigField: 123, 937 } 938 jsonStr, err := json.Marshal(config) 939 if err != nil { 940 t.Fatalf("failed to marshal config: %v", err) 941 } 942 if err := os.WriteFile(configPath, jsonStr, 0666); err != nil { 943 t.Fatalf("failed to write config file: %v", err) 944 } 945 946 ctx := context.Background() 947 948 ds, err := NewStoreFromDocker(StoreOptions{AllowPlaintextPut: true}) 949 if err != nil { 950 t.Fatal("NewStoreFromDocker() error =", err) 951 } 952 953 // test getting an existing credential 954 got, err := ds.Get(ctx, serverAddr1) 955 if err != nil { 956 t.Fatal("DynamicStore.Get() error =", err) 957 } 958 if want := cred1; got != want { 959 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 960 } 961 962 // test putting a new credential 963 serverAddr2 := "newtest.example.com" 964 cred2 := auth.Credential{ 965 Username: "username", 966 Password: "password", 967 } 968 if err := ds.Put(ctx, serverAddr2, cred2); err != nil { 969 t.Fatal("DynamicStore.Get() error =", err) 970 } 971 972 // test getting the new credential 973 got, err = ds.Get(ctx, serverAddr2) 974 if err != nil { 975 t.Fatal("DynamicStore.Get() error =", err) 976 } 977 if want := cred2; got != want { 978 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 979 } 980 981 // test deleting the old credential 982 err = ds.Delete(ctx, serverAddr1) 983 if err != nil { 984 t.Fatal("DynamicStore.Delete() error =", err) 985 } 986 987 // verify delete 988 got, err = ds.Get(ctx, serverAddr1) 989 if err != nil { 990 t.Fatal("DynamicStore.Get() error =", err) 991 } 992 if want := auth.EmptyCredential; got != want { 993 t.Errorf("DynamicStore.Get() = %v, want %v", got, want) 994 } 995 }