github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/cloud/credentials_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package cloud_test 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "regexp" 11 12 "github.com/juju/errors" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils/v3" 15 gc "gopkg.in/check.v1" 16 17 "github.com/juju/juju/cloud" 18 "github.com/juju/juju/testing" 19 ) 20 21 type credentialsSuite struct { 22 testing.FakeJujuXDGDataHomeSuite 23 } 24 25 var _ = gc.Suite(&credentialsSuite{}) 26 27 func (s *credentialsSuite) TestMarshalAccessKey(c *gc.C) { 28 creds := map[string]cloud.CloudCredential{ 29 "aws": { 30 DefaultCredential: "default-cred", 31 DefaultRegion: "us-west-2", 32 AuthCredentials: map[string]cloud.Credential{ 33 "peter": cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{ 34 "access-key": "key", 35 "secret-key": "secret", 36 }), 37 // TODO(wallyworld) - add anther credential once goyaml.v2 supports inline MapSlice. 38 //"paul": &cloud.AccessKeyCredentials{ 39 // Key: "paulkey", 40 // Secret: "paulsecret", 41 //}, 42 }, 43 }, 44 } 45 out, err := cloud.MarshalCredentials(creds) 46 c.Assert(err, jc.ErrorIsNil) 47 c.Assert(string(out), gc.Equals, ` 48 credentials: 49 aws: 50 default-credential: default-cred 51 default-region: us-west-2 52 peter: 53 auth-type: access-key 54 access-key: key 55 secret-key: secret 56 `[1:]) 57 } 58 59 func (s *credentialsSuite) TestMarshalOpenstackAccessKey(c *gc.C) { 60 creds := map[string]cloud.CloudCredential{ 61 "openstack": { 62 DefaultCredential: "default-cred", 63 DefaultRegion: "region-a", 64 AuthCredentials: map[string]cloud.Credential{ 65 "peter": cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{ 66 "access-key": "key", 67 "secret-key": "secret", 68 "tenant-name": "tenant", 69 }), 70 }, 71 }, 72 } 73 out, err := cloud.MarshalCredentials(creds) 74 c.Assert(err, jc.ErrorIsNil) 75 c.Assert(string(out), gc.Equals, ` 76 credentials: 77 openstack: 78 default-credential: default-cred 79 default-region: region-a 80 peter: 81 auth-type: access-key 82 access-key: key 83 secret-key: secret 84 tenant-name: tenant 85 `[1:]) 86 } 87 88 func (s *credentialsSuite) TestMarshalOpenstackUserPass(c *gc.C) { 89 creds := map[string]cloud.CloudCredential{ 90 "openstack": { 91 DefaultCredential: "default-cred", 92 DefaultRegion: "region-a", 93 AuthCredentials: map[string]cloud.Credential{ 94 "peter": cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 95 "username": "user", 96 "password": "secret", 97 "tenant-name": "tenant", 98 }), 99 }, 100 }, 101 } 102 out, err := cloud.MarshalCredentials(creds) 103 c.Assert(err, jc.ErrorIsNil) 104 c.Assert(string(out), gc.Equals, ` 105 credentials: 106 openstack: 107 default-credential: default-cred 108 default-region: region-a 109 peter: 110 auth-type: userpass 111 password: secret 112 tenant-name: tenant 113 username: user 114 `[1:]) 115 } 116 117 func (s *credentialsSuite) TestMarshalAzureCredntials(c *gc.C) { 118 creds := map[string]cloud.CloudCredential{ 119 "azure": { 120 DefaultCredential: "default-cred", 121 DefaultRegion: "Central US", 122 AuthCredentials: map[string]cloud.Credential{ 123 "peter": cloud.NewCredential(cloud.UserPassAuthType, map[string]string{ 124 "application-id": "app-id", 125 "application-password": "app-secret", 126 "subscription-id": "subscription-id", 127 "tenant-id": "tenant-id", 128 }), 129 }, 130 }, 131 } 132 out, err := cloud.MarshalCredentials(creds) 133 c.Assert(err, jc.ErrorIsNil) 134 c.Assert(string(out), gc.Equals, ` 135 credentials: 136 azure: 137 default-credential: default-cred 138 default-region: Central US 139 peter: 140 auth-type: userpass 141 application-id: app-id 142 application-password: app-secret 143 subscription-id: subscription-id 144 tenant-id: tenant-id 145 `[1:]) 146 } 147 148 func (s *credentialsSuite) TestMarshalOAuth1(c *gc.C) { 149 creds := map[string]cloud.CloudCredential{ 150 "maas": { 151 DefaultCredential: "default-cred", 152 DefaultRegion: "region-default", 153 AuthCredentials: map[string]cloud.Credential{ 154 "peter": cloud.NewCredential(cloud.OAuth1AuthType, map[string]string{ 155 "consumer-key": "consumer-key", 156 "consumer-secret": "consumer-secret", 157 "access-token": "access-token", 158 "token-secret": "token-secret", 159 }), 160 }, 161 }, 162 } 163 out, err := cloud.MarshalCredentials(creds) 164 c.Assert(err, jc.ErrorIsNil) 165 c.Assert(string(out), gc.Equals, ` 166 credentials: 167 maas: 168 default-credential: default-cred 169 default-region: region-default 170 peter: 171 auth-type: oauth1 172 access-token: access-token 173 consumer-key: consumer-key 174 consumer-secret: consumer-secret 175 token-secret: token-secret 176 `[1:]) 177 } 178 179 func (s *credentialsSuite) TestMarshalOAuth2(c *gc.C) { 180 creds := map[string]cloud.CloudCredential{ 181 "google": { 182 DefaultCredential: "default-cred", 183 DefaultRegion: "West US", 184 AuthCredentials: map[string]cloud.Credential{ 185 "peter": cloud.NewCredential(cloud.OAuth2AuthType, map[string]string{ 186 "client-id": "client-id", 187 "client-email": "client-email", 188 "private-key": "secret", 189 }), 190 }, 191 }, 192 } 193 out, err := cloud.MarshalCredentials(creds) 194 c.Assert(err, jc.ErrorIsNil) 195 c.Assert(string(out), gc.Equals, ` 196 credentials: 197 google: 198 default-credential: default-cred 199 default-region: West US 200 peter: 201 auth-type: oauth2 202 client-email: client-email 203 client-id: client-id 204 private-key: secret 205 `[1:]) 206 } 207 208 func (s *credentialsSuite) TestParseCredentials(c *gc.C) { 209 s.testParseCredentials(c, []byte(` 210 credentials: 211 aws: 212 default-credential: peter 213 default-region: us-east-2 214 peter: 215 auth-type: access-key 216 access-key: key 217 secret-key: secret 218 aws-china: 219 default-credential: zhu8jie 220 zhu8jie: 221 auth-type: access-key 222 access-key: key 223 secret-key: secret 224 sun5kong: 225 auth-type: access-key 226 access-key: quay 227 secret-key: sekrit 228 aws-gov: 229 default-region: us-gov-west-1 230 supersekrit: 231 auth-type: access-key 232 access-key: super 233 secret-key: sekrit 234 `[1:]), map[string]cloud.CloudCredential{ 235 "aws": { 236 DefaultCredential: "peter", 237 DefaultRegion: "us-east-2", 238 AuthCredentials: map[string]cloud.Credential{ 239 "peter": cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{ 240 "access-key": "key", 241 "secret-key": "secret", 242 }), 243 }, 244 }, 245 "aws-china": { 246 DefaultCredential: "zhu8jie", 247 AuthCredentials: map[string]cloud.Credential{ 248 "zhu8jie": cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{ 249 "access-key": "key", 250 "secret-key": "secret", 251 }), 252 "sun5kong": cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{ 253 "access-key": "quay", 254 "secret-key": "sekrit", 255 }), 256 }, 257 }, 258 "aws-gov": { 259 DefaultRegion: "us-gov-west-1", 260 AuthCredentials: map[string]cloud.Credential{ 261 "supersekrit": cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{ 262 "access-key": "super", 263 "secret-key": "sekrit", 264 }), 265 }, 266 }, 267 }) 268 } 269 270 func (s *credentialsSuite) TestParseCredentialsUnknownAuthType(c *gc.C) { 271 // Unknown auth-type is not validated by ParseCredentials. 272 // Validation is deferred to FinalizeCredential. 273 s.testParseCredentials(c, []byte(` 274 credentials: 275 cloud-name: 276 credential-name: 277 auth-type: woop 278 `[1:]), map[string]cloud.CloudCredential{ 279 "cloud-name": { 280 AuthCredentials: map[string]cloud.Credential{ 281 "credential-name": cloud.NewCredential("woop", nil), 282 }, 283 }, 284 }) 285 } 286 287 func (s *credentialsSuite) testParseCredentials(c *gc.C, input []byte, expect map[string]cloud.CloudCredential) { 288 output, err := cloud.ParseCredentials(input) 289 c.Assert(err, jc.ErrorIsNil) 290 c.Assert(output, jc.DeepEquals, expect) 291 } 292 293 func (s *credentialsSuite) TestParseCredentialsMissingAuthType(c *gc.C) { 294 s.testParseCredentialsError(c, []byte(` 295 credentials: 296 cloud-name: 297 credential-name: 298 doesnt: really-matter 299 `[1:]), "credentials.cloud-name.credential-name: missing auth-type") 300 } 301 302 func (s *credentialsSuite) TestParseCredentialsNonStringValue(c *gc.C) { 303 s.testParseCredentialsError(c, []byte(` 304 credentials: 305 cloud-name: 306 credential-name: 307 non-string-value: 123 308 `[1:]), `credentials\.cloud-name\.credential-name\.non-string-value: expected string, got int\(123\)`) 309 } 310 311 func (s *credentialsSuite) testParseCredentialsError(c *gc.C, input []byte, expect string) { 312 _, err := cloud.ParseCredentials(input) 313 c.Assert(err, gc.ErrorMatches, expect) 314 } 315 316 func (s *credentialsSuite) TestFinalizeCredential(c *gc.C) { 317 cred := cloud.NewCredential( 318 cloud.UserPassAuthType, 319 map[string]string{ 320 "key": "value", 321 }, 322 ) 323 schema := cloud.CredentialSchema{{ 324 "key", 325 cloud.CredentialAttr{ 326 Description: "key credential", 327 Hidden: true, 328 }, 329 }} 330 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 331 cloud.UserPassAuthType: schema, 332 }, readFileNotSupported) 333 c.Assert(err, jc.ErrorIsNil) 334 } 335 336 func (s *credentialsSuite) TestFinalizeCredentialFileAttr(c *gc.C) { 337 cred := cloud.NewCredential( 338 cloud.UserPassAuthType, 339 map[string]string{ 340 "key-file": "path", 341 "quay": "value", 342 }, 343 ) 344 schema := cloud.CredentialSchema{{ 345 "key", 346 cloud.CredentialAttr{ 347 Description: "key credential", 348 Hidden: true, 349 FileAttr: "key-file", 350 }, 351 }, { 352 "quay", cloud.CredentialAttr{FileAttr: "quay-file"}, 353 }} 354 readFile := func(s string) ([]byte, error) { 355 c.Assert(s, gc.Equals, "path") 356 return []byte("file-value"), nil 357 } 358 newCred, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 359 cloud.UserPassAuthType: schema, 360 }, readFile) 361 c.Assert(err, jc.ErrorIsNil) 362 c.Assert(newCred.Attributes(), jc.DeepEquals, map[string]string{ 363 "key": "file-value", 364 "quay": "value", 365 }) 366 } 367 368 func (s *credentialsSuite) TestFinalizeCredentialFileEmpty(c *gc.C) { 369 cred := cloud.NewCredential( 370 cloud.UserPassAuthType, 371 map[string]string{ 372 "key-file": "path", 373 }, 374 ) 375 schema := cloud.CredentialSchema{{ 376 "key", 377 cloud.CredentialAttr{ 378 Description: "key credential", 379 Hidden: true, 380 FileAttr: "key-file", 381 }, 382 }} 383 readFile := func(string) ([]byte, error) { 384 return nil, nil 385 } 386 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 387 cloud.UserPassAuthType: schema, 388 }, readFile) 389 c.Assert(err, gc.ErrorMatches, `empty file for "key" not valid`) 390 } 391 392 func (s *credentialsSuite) TestFinalizeCredentialFileAttrNeither(c *gc.C) { 393 cred := cloud.NewCredential( 394 cloud.UserPassAuthType, 395 map[string]string{}, 396 ) 397 schema := cloud.CredentialSchema{{ 398 "key", 399 cloud.CredentialAttr{ 400 Description: "key credential", 401 Hidden: true, 402 FileAttr: "key-file", 403 }, 404 }} 405 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 406 cloud.UserPassAuthType: schema, 407 }, readFileNotSupported) 408 c.Assert(err, gc.ErrorMatches, `either "key" or "key-file" must be specified`) 409 } 410 411 func (s *credentialsSuite) TestFinalizeCredentialFileAttrBoth(c *gc.C) { 412 cred := cloud.NewCredential( 413 cloud.UserPassAuthType, 414 map[string]string{ 415 "key": "value", 416 "key-file": "path", 417 }, 418 ) 419 schema := cloud.CredentialSchema{{ 420 "key", 421 cloud.CredentialAttr{ 422 Description: "key credential", 423 Hidden: true, 424 FileAttr: "key-file", 425 }, 426 }} 427 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 428 cloud.UserPassAuthType: schema, 429 }, readFileNotSupported) 430 c.Assert(err, gc.ErrorMatches, `specifying both "key" and "key-file" not valid`) 431 } 432 433 func (s *credentialsSuite) TestFinalizeCredentialInvalid(c *gc.C) { 434 cred := cloud.NewCredential( 435 cloud.UserPassAuthType, 436 map[string]string{}, 437 ) 438 schema := cloud.CredentialSchema{{ 439 "key", 440 cloud.CredentialAttr{ 441 Description: "key credential", 442 Hidden: true, 443 }, 444 }} 445 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 446 cloud.UserPassAuthType: schema, 447 }, readFileNotSupported) 448 c.Assert(err, gc.ErrorMatches, "key: expected string, got nothing") 449 } 450 451 func (s *credentialsSuite) TestFinalizeCredentialNotSupported(c *gc.C) { 452 cred := cloud.NewCredential( 453 cloud.OAuth2AuthType, 454 map[string]string{}, 455 ) 456 _, err := cloud.FinalizeCredential( 457 cred, map[cloud.AuthType]cloud.CredentialSchema{}, readFileNotSupported, 458 ) 459 c.Assert(err, jc.Satisfies, errors.IsNotSupported) 460 c.Assert(err, gc.ErrorMatches, `auth-type "oauth2" not supported`) 461 } 462 463 func readFileNotSupported(f string) ([]byte, error) { 464 return nil, errors.NotSupportedf("reading file %q", f) 465 } 466 467 func (s *credentialsSuite) TestFinalizeCredentialMandatoryFieldMissing(c *gc.C) { 468 cred := cloud.NewCredential( 469 cloud.UserPassAuthType, 470 map[string]string{ 471 "password": "secret", 472 "domain": "domain", 473 }, 474 ) 475 schema := cloud.CredentialSchema{ 476 {"username", cloud.CredentialAttr{Optional: false}}, 477 {"password", cloud.CredentialAttr{Hidden: true}}, 478 {"domain", cloud.CredentialAttr{}}, 479 } 480 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 481 cloud.UserPassAuthType: schema, 482 }, nil) 483 c.Assert(err, gc.ErrorMatches, "username: expected string, got nothing") 484 } 485 486 func (s *credentialsSuite) TestFinalizeCredentialMandatoryFieldFromFile(c *gc.C) { 487 cred := cloud.NewCredential( 488 cloud.UserPassAuthType, 489 map[string]string{ 490 "key-file": "path", 491 }, 492 ) 493 schema := cloud.CredentialSchema{{ 494 "key", 495 cloud.CredentialAttr{ 496 Description: "key credential", 497 Optional: false, 498 FileAttr: "key-file", 499 }, 500 }} 501 readFile := func(s string) ([]byte, error) { 502 c.Assert(s, gc.Equals, "path") 503 return []byte("file-value"), nil 504 } 505 newCred, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 506 cloud.UserPassAuthType: schema, 507 }, readFile) 508 c.Assert(err, jc.ErrorIsNil) 509 c.Assert(newCred.Attributes(), jc.DeepEquals, map[string]string{ 510 "key": "file-value", 511 }) 512 } 513 514 func (s *credentialsSuite) TestFinalizeCredentialExtraField(c *gc.C) { 515 cred := cloud.NewCredential( 516 cloud.UserPassAuthType, 517 map[string]string{ 518 "username": "user", 519 "password": "secret", 520 "domain": "domain", 521 "access-key": "access-key", 522 }, 523 ) 524 schema := cloud.CredentialSchema{ 525 {"username", cloud.CredentialAttr{Optional: false}}, 526 {"password", cloud.CredentialAttr{Hidden: true}}, 527 {"domain", cloud.CredentialAttr{}}, 528 } 529 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 530 cloud.UserPassAuthType: schema, 531 }, nil) 532 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`unknown key "access-key" (value "access-key")`)) 533 } 534 535 func (s *credentialsSuite) TestFinalizeCredentialInvalidChoice(c *gc.C) { 536 cred := cloud.NewCredential( 537 cloud.UserPassAuthType, 538 map[string]string{ 539 "username": "user", 540 "password": "secret", 541 "algorithm": "foo", 542 }, 543 ) 544 schema := cloud.CredentialSchema{ 545 {"username", cloud.CredentialAttr{Optional: false}}, 546 {"password", cloud.CredentialAttr{Hidden: true}}, 547 {"algorithm", cloud.CredentialAttr{Options: []interface{}{"bar", "foobar"}}}, 548 } 549 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 550 cloud.UserPassAuthType: schema, 551 }, nil) 552 c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(`algorithm: expected one of [bar foobar], got "foo"`)) 553 } 554 555 func (s *credentialsSuite) TestFinalizeCredentialFilePath(c *gc.C) { 556 dir := c.MkDir() 557 filename := filepath.Join(dir, "filename") 558 err := os.WriteFile(filename, []byte{}, 0600) 559 c.Assert(err, jc.ErrorIsNil) 560 561 cred := cloud.NewCredential( 562 cloud.JSONFileAuthType, 563 map[string]string{ 564 "file": filename, 565 }, 566 ) 567 schema := cloud.CredentialSchema{{ 568 "file", cloud.CredentialAttr{FilePath: true}, 569 }} 570 571 readFile := func(path string) ([]byte, error) { 572 c.Assert(path, gc.Equals, filename) 573 return []byte("file-contents"), nil 574 } 575 576 newCred, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 577 cloud.JSONFileAuthType: schema, 578 }, readFile) 579 c.Assert(err, jc.ErrorIsNil) 580 c.Assert(newCred.Attributes(), jc.DeepEquals, map[string]string{ 581 "file": "file-contents", 582 }) 583 } 584 585 func (s *credentialsSuite) TestFinalizeCredentialRelativeFilePath(c *gc.C) { 586 absFilename := filepath.Join(utils.Home(), "filename") 587 err := os.WriteFile(absFilename, []byte{}, 0600) 588 c.Assert(err, jc.ErrorIsNil) 589 590 cred := cloud.NewCredential( 591 cloud.JSONFileAuthType, 592 map[string]string{ 593 "file": "~/filename", 594 }, 595 ) 596 schema := cloud.CredentialSchema{{ 597 "file", cloud.CredentialAttr{FilePath: true}, 598 }} 599 readFile := func(path string) ([]byte, error) { 600 c.Assert(path, gc.Equals, absFilename) 601 return []byte("file-contents"), nil 602 } 603 newCred, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 604 cloud.JSONFileAuthType: schema, 605 }, readFile) 606 c.Assert(err, jc.ErrorIsNil) 607 c.Assert(newCred.Attributes(), jc.DeepEquals, map[string]string{ 608 "file": "file-contents", 609 }) 610 } 611 612 func (s *credentialsSuite) TestFinalizeCredentialInvalidFilePath(c *gc.C) { 613 fp := filepath.Join(c.MkDir(), "somefile") 614 cred := cloud.NewCredential( 615 cloud.JSONFileAuthType, 616 map[string]string{ 617 "file": fp, 618 }, 619 ) 620 schema := cloud.CredentialSchema{{ 621 "file", cloud.CredentialAttr{FilePath: true}, 622 }} 623 _, err := cloud.FinalizeCredential(cred, map[cloud.AuthType]cloud.CredentialSchema{ 624 cloud.JSONFileAuthType: schema, 625 }, nil) 626 c.Assert(err, gc.ErrorMatches, "invalid file path: .*") 627 } 628 629 func (s *credentialsSuite) TestRemoveSecrets(c *gc.C) { 630 cred := cloud.NewCredential( 631 cloud.UserPassAuthType, 632 map[string]string{ 633 "username": "user", 634 "password": "secret", 635 }, 636 ) 637 c.Assert(cred.Revoked, jc.IsFalse) 638 schema := cloud.CredentialSchema{{ 639 "username", cloud.CredentialAttr{}, 640 }, { 641 "password", cloud.CredentialAttr{Hidden: true}, 642 }} 643 sanitisedCred, err := cloud.RemoveSecrets(cred, map[cloud.AuthType]cloud.CredentialSchema{ 644 cloud.UserPassAuthType: schema, 645 }) 646 c.Assert(err, jc.ErrorIsNil) 647 c.Assert(sanitisedCred.Attributes(), jc.DeepEquals, map[string]string{ 648 "username": "user", 649 }) 650 } 651 652 func (s *credentialsSuite) TestValidateFileAttrValue(c *gc.C) { 653 _, err := cloud.ValidateFileAttrValue("/xyz/nothing.blah") 654 c.Assert(err, gc.ErrorMatches, "invalid file path: stat /xyz/nothing.blah: no such file or directory") 655 656 absPathNewFile := filepath.Join(utils.Home(), "new-creds.json") 657 err = os.WriteFile(absPathNewFile, []byte("abc"), 0600) 658 c.Assert(err, jc.ErrorIsNil) 659 660 absPath, err := cloud.ValidateFileAttrValue("~/new-creds.json") 661 c.Assert(err, jc.ErrorIsNil) 662 c.Assert(absPath, gc.Equals, absPathNewFile) 663 664 _, err = cloud.ValidateFileAttrValue(utils.Home()) 665 c.Assert(err, gc.ErrorMatches, fmt.Sprintf("file path %q must be a file", utils.Home())) 666 } 667 668 func (s *credentialsSuite) TestExpandFilePathsOfCredential(c *gc.C) { 669 tempFile, err := os.CreateTemp("", "") 670 c.Assert(err, jc.ErrorIsNil) 671 672 _, err = tempFile.WriteString("test") 673 c.Assert(err, jc.ErrorIsNil) 674 675 c.Assert(tempFile.Close(), jc.ErrorIsNil) 676 677 cred := cloud.NewNamedCredential("test", 678 cloud.AuthType("test"), 679 map[string]string{ 680 "test-key": tempFile.Name(), 681 "test-key1": "test-value", 682 }, 683 false, 684 ) 685 686 credSchema := cloud.CredentialSchema{ 687 { 688 Name: "test-key", 689 CredentialAttr: cloud.CredentialAttr{ 690 Description: "test credential attribute", 691 ExpandFilePath: true, 692 Hidden: false, 693 }, 694 }, 695 { 696 Name: "test-key1", 697 CredentialAttr: cloud.CredentialAttr{ 698 Description: "test credential attribute", 699 ExpandFilePath: false, 700 Hidden: false, 701 }, 702 }, 703 } 704 705 cred, err = cloud.ExpandFilePathsOfCredential( 706 cred, map[cloud.AuthType]cloud.CredentialSchema{ 707 cloud.AuthType("test"): credSchema, 708 }, 709 ) 710 c.Assert(err, jc.ErrorIsNil) 711 712 c.Assert(cred.Attributes()["test-key"], gc.Equals, "test") 713 c.Assert(cred.Attributes()["test-key1"], gc.Equals, "test-value") 714 } 715 716 // Regression test for lp1976620 717 func (s *credentialsSuite) TestExpandFilePathsOfPem(c *gc.C) { 718 testPemCert := ` 719 -----BEGIN CERTIFICATE----- 720 MIIB5DCCAWugAwIBAgIRAI0U9NoAVVolPG4O85Zr3dgwCgYIKoZIzj0EAwMwOjEc 721 MBoGA1UEChMTbGludXhjb250YWluZXJzLm9yZzEaMBgGA1UEAwwRdGxtQHRsbS1t 722 YnAubG9jYWwwHhcNMjIwMzAzMTMxNzMxWhcNMzIwMjI5MTMxNzMxWjA6MRwwGgYD 723 VQQKExNsaW51eGNvbnRhaW5lcnMub3JnMRowGAYDVQQDDBF0bG1AdGxtLW1icC5s 724 b2NhbDB2MBAGByqGSM49AgEGBSuBBAAiA2IABHvoqBLC2amlFuAQq/IrMUd4Cver 725 teYK/BkJfTOx5M6Gt+RE7Vi0uVO0MfzOPrtTKQQPtffSelyGtpxZtQjRLKhdzCa9 726 E2lDhIf/j6axT64cp3vdA3XU96pIfFH32Ff1yqM1MDMwDgYDVR0PAQH/BAQDAgWg 727 MBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwMD 728 ZwAwZAIwcac9nw5lXFtQyO9d5ZDUBfjafw/fg0YvaypV5KeRhC/ljB4ooN+DuJjy 729 L8jEeLyWAjA53jxIoL5A5CyXKhqQPcYyfTMHstcP7ip8wLMnee1y3b8wwq9celAa 730 QD0jIrgXpik= 731 -----END CERTIFICATE----- 732 ` 733 734 cred := cloud.NewNamedCredential("test", 735 cloud.AuthType("test"), 736 map[string]string{ 737 "test-key": testPemCert, 738 }, 739 false, 740 ) 741 742 credSchema := cloud.CredentialSchema{ 743 { 744 Name: "test-key", 745 CredentialAttr: cloud.CredentialAttr{ 746 Description: "test credential attribute", 747 ExpandFilePath: true, 748 Hidden: false, 749 }, 750 }, 751 } 752 753 cred, err := cloud.ExpandFilePathsOfCredential( 754 cred, map[cloud.AuthType]cloud.CredentialSchema{ 755 cloud.AuthType("test"): credSchema, 756 }, 757 ) 758 c.Assert(err, jc.ErrorIsNil) 759 c.Assert(cred.Attributes()["test-key"], gc.Equals, testPemCert) 760 }