619 625 |
|
620 626 | make_test!(assume_role_override_global_env_url);
|
621 627 | make_test!(assume_role_override_service_env_url);
|
622 628 | make_test!(assume_role_override_global_profile_url);
|
623 629 | make_test!(assume_role_override_service_profile_url);
|
624 630 | }
|
625 631 |
|
626 632 | #[cfg(all(test, feature = "sso"))]
|
627 633 | mod sso_tests {
|
628 634 | use crate::{profile::credentials::Builder, provider_config::ProviderConfig};
|
635 + | use aws_credential_types::credential_feature::AwsCredentialFeature;
|
629 636 | use aws_credential_types::provider::ProvideCredentials;
|
630 637 | use aws_sdk_sso::config::RuntimeComponents;
|
631 638 | use aws_smithy_runtime_api::client::{
|
632 639 | http::{
|
633 640 | HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings,
|
634 641 | SharedHttpConnector,
|
635 642 | },
|
636 643 | orchestrator::{HttpRequest, HttpResponse},
|
637 644 | };
|
638 645 | use aws_smithy_types::body::SdkBody;
|
639 646 | use aws_types::os_shim_internal::{Env, Fs};
|
640 647 | use std::collections::HashMap;
|
641 648 |
|
642 - | // TODO(https://github.com/awslabs/aws-sdk-rust/issues/1117) This test is ignored on Windows because it uses Unix-style paths
|
643 - | #[cfg_attr(windows, ignore)]
|
644 - | // In order to preserve the SSO token cache, the inner provider must only
|
645 - | // be created once, rather than once per credential resolution.
|
646 - | #[tokio::test]
|
647 - | async fn create_inner_provider_exactly_once() {
|
648 - | #[derive(Debug)]
|
649 - | struct ClientInner {
|
650 - | expected_token: &'static str,
|
651 - | }
|
652 - | impl HttpConnector for ClientInner {
|
653 - | fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
|
654 - | assert_eq!(
|
655 - | self.expected_token,
|
656 - | request.headers().get("x-amz-sso_bearer_token").unwrap()
|
657 - | );
|
658 - | HttpConnectorFuture::ready(Ok(HttpResponse::new(
|
649 + | #[derive(Debug)]
|
650 + | struct ClientInner {
|
651 + | expected_token: &'static str,
|
652 + | }
|
653 + | impl HttpConnector for ClientInner {
|
654 + | fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
|
655 + | assert_eq!(
|
656 + | self.expected_token,
|
657 + | request.headers().get("x-amz-sso_bearer_token").unwrap()
|
658 + | );
|
659 + | HttpConnectorFuture::ready(Ok(HttpResponse::new(
|
659 660 | 200.try_into().unwrap(),
|
660 661 | SdkBody::from("{\"roleCredentials\":{\"accessKeyId\":\"ASIARTESTID\",\"secretAccessKey\":\"TESTSECRETKEY\",\"sessionToken\":\"TESTSESSIONTOKEN\",\"expiration\": 1651516560000}}"),
|
661 662 | )))
|
662 - | }
|
663 663 | }
|
664 - | #[derive(Debug)]
|
665 - | struct Client {
|
666 - | inner: SharedHttpConnector,
|
667 - | }
|
668 - | impl Client {
|
669 - | fn new(expected_token: &'static str) -> Self {
|
670 - | Self {
|
671 - | inner: SharedHttpConnector::new(ClientInner { expected_token }),
|
672 - | }
|
664 + | }
|
665 + | #[derive(Debug)]
|
666 + | struct Client {
|
667 + | inner: SharedHttpConnector,
|
668 + | }
|
669 + | impl Client {
|
670 + | fn new(expected_token: &'static str) -> Self {
|
671 + | Self {
|
672 + | inner: SharedHttpConnector::new(ClientInner { expected_token }),
|
673 673 | }
|
674 674 | }
|
675 - | impl HttpClient for Client {
|
676 - | fn http_connector(
|
677 - | &self,
|
678 - | _settings: &HttpConnectorSettings,
|
679 - | _components: &RuntimeComponents,
|
680 - | ) -> SharedHttpConnector {
|
681 - | self.inner.clone()
|
682 - | }
|
675 + | }
|
676 + | impl HttpClient for Client {
|
677 + | fn http_connector(
|
678 + | &self,
|
679 + | _settings: &HttpConnectorSettings,
|
680 + | _components: &RuntimeComponents,
|
681 + | ) -> SharedHttpConnector {
|
682 + | self.inner.clone()
|
683 683 | }
|
684 + | }
|
684 685 |
|
685 - | let fs = Fs::from_map({
|
686 + | fn create_test_fs() -> Fs {
|
687 + | Fs::from_map({
|
686 688 | let mut map = HashMap::new();
|
687 689 | map.insert(
|
688 690 | "/home/.aws/config".to_string(),
|
689 691 | br#"
|
690 692 | [profile default]
|
691 693 | sso_session = dev
|
692 694 | sso_account_id = 012345678901
|
693 695 | sso_role_name = SampleRole
|
694 696 | region = us-east-1
|
695 697 |
|
696 698 | [sso-session dev]
|
697 699 | sso_region = us-east-1
|
698 700 | sso_start_url = https://d-abc123.awsapps.com/start
|
699 701 | "#
|
700 702 | .to_vec(),
|
701 703 | );
|
702 704 | map.insert(
|
703 705 | "/home/.aws/sso/cache/34c6fceca75e456f25e7e99531e2425c6c1de443.json".to_string(),
|
704 706 | br#"
|
705 707 | {
|
706 708 | "accessToken": "secret-access-token",
|
707 709 | "expiresAt": "2199-11-14T04:05:45Z",
|
708 710 | "refreshToken": "secret-refresh-token",
|
709 711 | "clientId": "ABCDEFG323242423121312312312312312",
|
710 712 | "clientSecret": "ABCDE123",
|
711 713 | "registrationExpiresAt": "2199-03-06T19:53:17Z",
|
712 714 | "region": "us-east-1",
|
713 715 | "startUrl": "https://d-abc123.awsapps.com/start"
|
714 716 | }
|
715 717 | "#
|
716 718 | .to_vec(),
|
717 719 | );
|
718 720 | map
|
719 - | });
|
721 + | })
|
722 + | }
|
723 + |
|
724 + | // TODO(https://github.com/awslabs/aws-sdk-rust/issues/1117) This test is ignored on Windows because it uses Unix-style paths
|
725 + | #[cfg_attr(windows, ignore)]
|
726 + | // In order to preserve the SSO token cache, the inner provider must only
|
727 + | // be created once, rather than once per credential resolution.
|
728 + | #[tokio::test]
|
729 + | async fn create_inner_provider_exactly_once() {
|
730 + | let fs = create_test_fs();
|
731 + |
|
720 732 | let provider_config = ProviderConfig::empty()
|
721 733 | .with_fs(fs.clone())
|
722 734 | .with_env(Env::from_slice(&[("HOME", "/home")]))
|
723 735 | .with_http_client(Client::new("secret-access-token"));
|
724 736 | let provider = Builder::default().configure(&provider_config).build();
|
725 737 |
|
726 738 | let first_creds = provider.provide_credentials().await.unwrap();
|
727 739 |
|
728 740 | // Write to the token cache with an access token that won't match the fake client's
|
729 741 | // expected access token, and thus, won't return SSO credentials.
|
730 742 | fs.write(
|
731 743 | "/home/.aws/sso/cache/34c6fceca75e456f25e7e99531e2425c6c1de443.json",
|
732 744 | r#"
|
733 745 | {
|
734 746 | "accessToken": "NEW!!secret-access-token",
|
735 747 | "expiresAt": "2199-11-14T04:05:45Z",
|
736 748 | "refreshToken": "secret-refresh-token",
|
737 749 | "clientId": "ABCDEFG323242423121312312312312312",
|
738 750 | "clientSecret": "ABCDE123",
|
739 751 | "registrationExpiresAt": "2199-03-06T19:53:17Z",
|
740 752 | "region": "us-east-1",
|
741 753 | "startUrl": "https://d-abc123.awsapps.com/start"
|
742 754 | }
|
743 755 | "#,
|
744 756 | )
|
745 757 | .await
|
746 758 | .unwrap();
|
747 759 |
|
748 760 | // Loading credentials will still work since the SSOTokenProvider should have only
|
749 761 | // been created once, and thus, the correct token is still in an in-memory cache.
|
750 762 | let second_creds = provider
|
751 763 | .provide_credentials()
|
752 764 | .await
|
753 765 | .expect("used cached token instead of loading from the file system");
|
754 766 | assert_eq!(first_creds, second_creds);
|
755 767 |
|
756 768 | // Now create a new provider, which should use the new cached token value from the file system
|
757 769 | // since it won't have the in-memory cache. We do this just to verify that the FS mutation above
|
758 770 | // actually worked correctly.
|
759 771 | let provider_config = ProviderConfig::empty()
|
760 772 | .with_fs(fs.clone())
|
761 773 | .with_env(Env::from_slice(&[("HOME", "/home")]))
|
762 774 | .with_http_client(Client::new("NEW!!secret-access-token"));
|
763 775 | let provider = Builder::default().configure(&provider_config).build();
|
764 776 | let third_creds = provider.provide_credentials().await.unwrap();
|
765 777 | assert_eq!(second_creds, third_creds);
|
766 778 | }
|
779 + |
|
780 + | #[cfg_attr(windows, ignore)]
|
781 + | #[tokio::test]
|
782 + | async fn credential_feature() {
|
783 + | let fs = create_test_fs();
|
784 + |
|
785 + | let provider_config = ProviderConfig::empty()
|
786 + | .with_fs(fs.clone())
|
787 + | .with_env(Env::from_slice(&[("HOME", "/home")]))
|
788 + | .with_http_client(Client::new("secret-access-token"));
|
789 + | let provider = Builder::default().configure(&provider_config).build();
|
790 + |
|
791 + | let creds = provider.provide_credentials().await.unwrap();
|
792 + |
|
793 + | assert_eq!(
|
794 + | &vec![
|
795 + | AwsCredentialFeature::CredentialsSso,
|
796 + | AwsCredentialFeature::CredentialsProfile
|
797 + | ],
|
798 + | creds.get_property::<Vec<AwsCredentialFeature>>().unwrap()
|
799 + | )
|
800 + | }
|
767 801 | }
|