aws_config/profile/
token.rs1use crate::profile::cell::ErrorTakingOnceCell;
9#[allow(deprecated)]
10use crate::profile::profile_file::ProfileFiles;
11use crate::profile::ProfileSet;
12use crate::provider_config::ProviderConfig;
13use crate::sso::SsoTokenProvider;
14use aws_credential_types::provider::{
15 error::TokenError, future, token::ProvideToken, token::Result as TokenResult,
16};
17use aws_types::{region::Region, SdkConfig};
18
19async fn load_profile_set(provider_config: &ProviderConfig) -> Result<&ProfileSet, TokenError> {
20 provider_config
21 .try_profile()
22 .await
23 .map_err(|parse_err| TokenError::invalid_configuration(parse_err.clone()))
24}
25
26fn create_token_provider(
27 sdk_config: &SdkConfig,
28 provider_config: &ProviderConfig,
29 profile_set: &ProfileSet,
30) -> Result<SsoTokenProvider, TokenError> {
31 let repr = crate::profile::credentials::repr::resolve_chain(profile_set)
32 .map_err(TokenError::invalid_configuration)?;
33 match repr.base {
34 crate::profile::credentials::repr::BaseProvider::Sso {
35 sso_session_name,
36 sso_region,
37 sso_start_url,
38 ..
39 } => {
40 let mut builder = SsoTokenProvider::builder().configure(sdk_config);
41 builder.set_session_name(sso_session_name.map(|s| s.to_string()));
42 Ok(builder
43 .region(Region::new(sso_region.to_string()))
44 .start_url(sso_start_url)
45 .build_with(provider_config.env(), provider_config.fs()))
46 }
47 _ => Err(TokenError::not_loaded(
48 "no sso-session configured in profile file",
49 )),
50 }
51}
52
53#[derive(Debug)]
79pub struct ProfileFileTokenProvider {
80 sdk_config: SdkConfig,
81 provider_config: ProviderConfig,
82 inner_provider: ErrorTakingOnceCell<SsoTokenProvider, TokenError>,
83}
84
85impl ProfileFileTokenProvider {
86 pub fn builder() -> Builder {
88 Builder::default()
89 }
90
91 async fn load_token(&self) -> TokenResult {
92 let inner_provider = self
93 .inner_provider
94 .get_or_init(
95 {
96 let sdk_config = self.sdk_config.clone();
97 let provider_config = self.provider_config.clone();
98 move || async move {
99 let profile_set = load_profile_set(&provider_config).await?;
100 create_token_provider(&sdk_config, &provider_config, profile_set)
101 }
102 },
103 TokenError::unhandled(
104 "profile file token provider initialization error already taken",
105 ),
106 )
107 .await?;
108
109 inner_provider.provide_token().await
110 }
111}
112
113impl ProvideToken for ProfileFileTokenProvider {
114 fn provide_token<'a>(&'a self) -> future::ProvideToken<'a>
115 where
116 Self: 'a,
117 {
118 future::ProvideToken::new(self.load_token())
119 }
120}
121
122#[derive(Debug, Default)]
124pub struct Builder {
125 provider_config: Option<ProviderConfig>,
126 profile_override: Option<String>,
127 #[allow(deprecated)]
128 profile_files: Option<ProfileFiles>,
129}
130
131impl Builder {
132 pub(crate) fn configure(mut self, provider_config: &ProviderConfig) -> Self {
134 self.provider_config = Some(provider_config.clone());
135 self
136 }
137
138 pub fn profile_name(mut self, profile_name: impl Into<String>) -> Self {
140 self.profile_override = Some(profile_name.into());
141 self
142 }
143
144 #[allow(deprecated)]
146 pub fn profile_files(mut self, profile_files: ProfileFiles) -> Self {
147 self.profile_files = Some(profile_files);
148 self
149 }
150
151 pub fn build(self) -> ProfileFileTokenProvider {
153 let build_span = tracing::debug_span!("build_profile_token_provider");
154 let _enter = build_span.enter();
155 let conf = self
156 .provider_config
157 .unwrap_or_default()
158 .with_profile_config(self.profile_files, self.profile_override);
159
160 ProfileFileTokenProvider {
161 sdk_config: conf.client_config(),
162 provider_config: conf,
163 inner_provider: ErrorTakingOnceCell::new(),
164 }
165 }
166}
167
168#[cfg(test)]
169mod test {
170 use aws_credential_types::provider::token::ProvideToken;
171
172 macro_rules! make_test {
192 ($name:ident $(#[$m:meta])*) => {
193 make_test!($name, execute, $(#[$m])*);
194 };
195 (update: $name:ident) => {
196 make_test!($name, execute_and_update);
197 };
198 (live: $name:ident) => {
199 make_test!($name, execute_from_live_traffic);
200 };
201 ($name:ident, $func:ident, $(#[$m:meta])*) => {
202 make_test!($name, $func, std::convert::identity $(, #[$m])*);
203 };
204 ($name:ident, builder: $provider_config_builder:expr) => {
205 make_test!($name, execute, $provider_config_builder);
206 };
207 ($name:ident, $func:ident, $provider_config_builder:expr $(, #[$m:meta])*) => {
208 $(#[$m])*
209 #[tokio::test]
210 async fn $name() {
211 let _ = crate::test_case::TestEnvironment::from_dir(
212 concat!(
213 "./test-data/default-token-provider-chain/",
214 stringify!($name)
215 ),
216 crate::test_case::test_token_provider(|config| {
217 async move {
218 crate::default_provider::token::Builder::default()
219 .configure(config)
220 .build()
221 .await
222 .provide_token()
223 .await
224 }
225 }),
226 )
227 .await
228 .unwrap()
229 .map_provider_config($provider_config_builder)
230 .$func()
231 .await;
232 }
233 };
234 }
235
236 make_test!(profile_keys);
237 make_test!(profile_keys_case_insensitive);
238 make_test!(profile_name);
239}