1use std::sync::Arc;
2
3#[cfg(feature = "async")]
4use std::{future::Future, pin::Pin};
5
6#[cfg(all(
7 any(feature = "async", feature = "blocking"),
8 any(feature = "credentials-imds", feature = "credentials-sts")
9))]
10use reqx::advanced::TlsRootStore;
11
12use crate::Result;
13
14use super::{Auth, Credentials};
15
16#[cfg(any(feature = "async", feature = "blocking"))]
17use super::CredentialsSnapshot;
18#[cfg(feature = "credentials-sts")]
19use super::Region;
20#[cfg(any(feature = "credentials-imds", feature = "credentials-sts"))]
21use super::cache::CachedProvider;
22
23#[cfg(feature = "async")]
24pub type CredentialsFuture<'a> =
26 Pin<Box<dyn Future<Output = Result<CredentialsSnapshot>> + Send + 'a>>;
27
28pub trait CredentialsProvider: std::fmt::Debug + Send + Sync {
34 #[cfg(feature = "async")]
36 fn credentials_async(&self) -> CredentialsFuture<'_>;
37
38 #[cfg(feature = "blocking")]
40 fn credentials_blocking(&self) -> Result<CredentialsSnapshot>;
41}
42
43pub type DynCredentialsProvider = Arc<dyn CredentialsProvider>;
45
46#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
48pub enum CredentialsTlsRootStore {
49 #[default]
54 BackendDefault,
55 WebPki,
57 System,
59}
60
61impl CredentialsTlsRootStore {
62 #[cfg(all(
63 any(feature = "async", feature = "blocking"),
64 any(feature = "credentials-imds", feature = "credentials-sts")
65 ))]
66 pub(crate) const fn into_reqx(self) -> TlsRootStore {
67 match self {
68 Self::BackendDefault => TlsRootStore::BackendDefault,
69 Self::WebPki => TlsRootStore::WebPki,
70 Self::System => TlsRootStore::System,
71 }
72 }
73}
74
75#[cfg(feature = "credentials-sts")]
76#[derive(Clone, Debug)]
77struct StaticCredentialsProvider {
78 snapshot: CredentialsSnapshot,
79}
80
81#[cfg(feature = "credentials-sts")]
82impl StaticCredentialsProvider {
83 fn new(credentials: Credentials) -> Self {
84 Self {
85 snapshot: CredentialsSnapshot::new(credentials),
86 }
87 }
88}
89
90#[cfg(feature = "credentials-sts")]
91impl CredentialsProvider for StaticCredentialsProvider {
92 #[cfg(feature = "async")]
93 fn credentials_async(&self) -> CredentialsFuture<'_> {
94 let snapshot = self.snapshot.clone();
95 Box::pin(async move { Ok(snapshot) })
96 }
97
98 #[cfg(feature = "blocking")]
99 fn credentials_blocking(&self) -> Result<CredentialsSnapshot> {
100 Ok(self.snapshot.clone())
101 }
102}
103
104#[cfg(feature = "credentials-imds")]
105#[derive(Debug, Clone, Copy)]
106struct ImdsProvider {
107 tls_root_store: CredentialsTlsRootStore,
108}
109
110#[cfg(feature = "credentials-imds")]
111impl CredentialsProvider for ImdsProvider {
112 #[cfg(feature = "async")]
113 fn credentials_async(&self) -> CredentialsFuture<'_> {
114 Box::pin(async move {
115 crate::credentials::imds::load_async(self.tls_root_store.into_reqx()).await
116 })
117 }
118
119 #[cfg(feature = "blocking")]
120 fn credentials_blocking(&self) -> Result<CredentialsSnapshot> {
121 crate::credentials::imds::load_blocking(self.tls_root_store.into_reqx())
122 }
123}
124
125#[cfg(feature = "credentials-sts")]
126#[derive(Debug)]
127struct StsAssumeRoleProvider {
128 region: Region,
129 role_arn: String,
130 role_session_name: String,
131 source: DynCredentialsProvider,
132 tls_root_store: CredentialsTlsRootStore,
133}
134
135#[cfg(feature = "credentials-sts")]
136impl CredentialsProvider for StsAssumeRoleProvider {
137 #[cfg(feature = "async")]
138 fn credentials_async(&self) -> CredentialsFuture<'_> {
139 Box::pin(async move {
140 let source = self.source.credentials_async().await?;
141 crate::credentials::sts::assume_role_async(
142 self.region.clone(),
143 self.role_arn.clone(),
144 self.role_session_name.clone(),
145 source.credentials().clone(),
146 self.tls_root_store.into_reqx(),
147 )
148 .await
149 })
150 }
151
152 #[cfg(feature = "blocking")]
153 fn credentials_blocking(&self) -> Result<CredentialsSnapshot> {
154 let source = self.source.credentials_blocking()?;
155 crate::credentials::sts::assume_role_blocking(
156 self.region.clone(),
157 self.role_arn.clone(),
158 self.role_session_name.clone(),
159 source.credentials().clone(),
160 self.tls_root_store.into_reqx(),
161 )
162 }
163}
164
165#[cfg(feature = "credentials-sts")]
166#[derive(Debug, Clone, Copy)]
167struct StsWebIdentityProvider {
168 tls_root_store: CredentialsTlsRootStore,
169}
170
171#[cfg(feature = "credentials-sts")]
172impl CredentialsProvider for StsWebIdentityProvider {
173 #[cfg(feature = "async")]
174 fn credentials_async(&self) -> CredentialsFuture<'_> {
175 Box::pin(async move {
176 crate::credentials::sts::assume_role_with_web_identity_env_async(
177 self.tls_root_store.into_reqx(),
178 )
179 .await
180 })
181 }
182
183 #[cfg(feature = "blocking")]
184 fn credentials_blocking(&self) -> Result<CredentialsSnapshot> {
185 crate::credentials::sts::assume_role_with_web_identity_env_blocking(
186 self.tls_root_store.into_reqx(),
187 )
188 }
189}
190
191impl Auth {
192 pub fn from_env() -> Result<Self> {
196 let access_key_id = crate::util::env::required_var("AWS_ACCESS_KEY_ID")?;
197 let secret_access_key = crate::util::env::required_var("AWS_SECRET_ACCESS_KEY")?;
198 let session_token = crate::util::env::optional_var("AWS_SESSION_TOKEN")?;
199
200 let mut creds = Credentials::new(access_key_id, secret_access_key)?;
201 if let Some(token) = session_token {
202 creds = creds.with_session_token(token)?;
203 }
204
205 Ok(Self::Static(creds))
206 }
207
208 pub fn provider(provider: DynCredentialsProvider) -> Self {
210 Self::Provider(provider)
211 }
212
213 #[cfg(feature = "credentials-profile")]
215 pub fn from_profile(profile: impl AsRef<str>) -> Result<Self> {
216 let creds = crate::credentials::profile::load_profile_credentials(profile.as_ref())?;
217 Ok(Self::Static(creds))
218 }
219
220 #[cfg(feature = "credentials-profile")]
222 pub fn from_profile_env() -> Result<Self> {
223 Self::from_profile(crate::credentials::profile::profile_from_env()?)
224 }
225
226 #[cfg(all(feature = "credentials-imds", feature = "async"))]
228 pub async fn from_imds() -> Result<Self> {
229 Self::from_imds_with_tls_root_store(CredentialsTlsRootStore::BackendDefault).await
230 }
231
232 #[cfg(all(feature = "credentials-imds", feature = "async"))]
234 pub async fn from_imds_with_tls_root_store(
235 tls_root_store: CredentialsTlsRootStore,
236 ) -> Result<Self> {
237 let initial = crate::credentials::imds::load_async(tls_root_store.into_reqx()).await?;
238 let provider = CachedProvider::new(ImdsProvider { tls_root_store }).with_initial(initial);
239 Ok(Self::Provider(Arc::new(provider)))
240 }
241
242 #[cfg(all(feature = "credentials-imds", feature = "blocking"))]
244 pub fn from_imds_blocking() -> Result<Self> {
245 Self::from_imds_blocking_with_tls_root_store(CredentialsTlsRootStore::BackendDefault)
246 }
247
248 #[cfg(all(feature = "credentials-imds", feature = "blocking"))]
250 pub fn from_imds_blocking_with_tls_root_store(
251 tls_root_store: CredentialsTlsRootStore,
252 ) -> Result<Self> {
253 let initial = crate::credentials::imds::load_blocking(tls_root_store.into_reqx())?;
254 let provider = CachedProvider::new(ImdsProvider { tls_root_store }).with_initial(initial);
255 Ok(Self::Provider(Arc::new(provider)))
256 }
257
258 #[cfg(all(feature = "credentials-sts", feature = "async"))]
260 pub async fn assume_role(
261 region: Region,
262 role_arn: impl Into<String>,
263 role_session_name: impl Into<String>,
264 source_credentials: Credentials,
265 ) -> Result<Self> {
266 Self::assume_role_with_tls_root_store(
267 region,
268 role_arn,
269 role_session_name,
270 source_credentials,
271 CredentialsTlsRootStore::BackendDefault,
272 )
273 .await
274 }
275
276 #[cfg(all(feature = "credentials-sts", feature = "async"))]
278 pub async fn assume_role_with_tls_root_store(
279 region: Region,
280 role_arn: impl Into<String>,
281 role_session_name: impl Into<String>,
282 source_credentials: Credentials,
283 tls_root_store: CredentialsTlsRootStore,
284 ) -> Result<Self> {
285 Self::assume_role_with_provider_with_tls_root_store(
286 region,
287 role_arn,
288 role_session_name,
289 Arc::new(StaticCredentialsProvider::new(source_credentials)),
290 tls_root_store,
291 )
292 .await
293 }
294
295 #[cfg(all(feature = "credentials-sts", feature = "blocking"))]
297 pub fn assume_role_blocking(
298 region: Region,
299 role_arn: impl Into<String>,
300 role_session_name: impl Into<String>,
301 source_credentials: Credentials,
302 ) -> Result<Self> {
303 Self::assume_role_blocking_with_tls_root_store(
304 region,
305 role_arn,
306 role_session_name,
307 source_credentials,
308 CredentialsTlsRootStore::BackendDefault,
309 )
310 }
311
312 #[cfg(all(feature = "credentials-sts", feature = "blocking"))]
314 pub fn assume_role_blocking_with_tls_root_store(
315 region: Region,
316 role_arn: impl Into<String>,
317 role_session_name: impl Into<String>,
318 source_credentials: Credentials,
319 tls_root_store: CredentialsTlsRootStore,
320 ) -> Result<Self> {
321 Self::assume_role_with_provider_blocking_with_tls_root_store(
322 region,
323 role_arn,
324 role_session_name,
325 Arc::new(StaticCredentialsProvider::new(source_credentials)),
326 tls_root_store,
327 )
328 }
329
330 #[cfg(all(feature = "credentials-sts", feature = "async"))]
332 pub async fn from_web_identity_env() -> Result<Self> {
333 Self::from_web_identity_env_with_tls_root_store(CredentialsTlsRootStore::BackendDefault)
334 .await
335 }
336
337 #[cfg(all(feature = "credentials-sts", feature = "async"))]
339 pub async fn from_web_identity_env_with_tls_root_store(
340 tls_root_store: CredentialsTlsRootStore,
341 ) -> Result<Self> {
342 let provider = StsWebIdentityProvider { tls_root_store };
343 let initial = provider.credentials_async().await?;
344 let provider = CachedProvider::new(provider).with_initial(initial);
345 Ok(Self::Provider(Arc::new(provider)))
346 }
347
348 #[cfg(all(feature = "credentials-sts", feature = "blocking"))]
350 pub fn from_web_identity_env_blocking() -> Result<Self> {
351 Self::from_web_identity_env_blocking_with_tls_root_store(
352 CredentialsTlsRootStore::BackendDefault,
353 )
354 }
355
356 #[cfg(all(feature = "credentials-sts", feature = "blocking"))]
358 pub fn from_web_identity_env_blocking_with_tls_root_store(
359 tls_root_store: CredentialsTlsRootStore,
360 ) -> Result<Self> {
361 let provider = StsWebIdentityProvider { tls_root_store };
362 let initial = provider.credentials_blocking()?;
363 let provider = CachedProvider::new(provider).with_initial(initial);
364 Ok(Self::Provider(Arc::new(provider)))
365 }
366
367 #[cfg(all(feature = "credentials-sts", feature = "async"))]
369 pub async fn assume_role_with_provider(
370 region: Region,
371 role_arn: impl Into<String>,
372 role_session_name: impl Into<String>,
373 source: DynCredentialsProvider,
374 ) -> Result<Self> {
375 Self::assume_role_with_provider_with_tls_root_store(
376 region,
377 role_arn,
378 role_session_name,
379 source,
380 CredentialsTlsRootStore::BackendDefault,
381 )
382 .await
383 }
384
385 #[cfg(all(feature = "credentials-sts", feature = "async"))]
387 pub async fn assume_role_with_provider_with_tls_root_store(
388 region: Region,
389 role_arn: impl Into<String>,
390 role_session_name: impl Into<String>,
391 source: DynCredentialsProvider,
392 tls_root_store: CredentialsTlsRootStore,
393 ) -> Result<Self> {
394 let provider = StsAssumeRoleProvider {
395 region,
396 role_arn: role_arn.into(),
397 role_session_name: role_session_name.into(),
398 source,
399 tls_root_store,
400 };
401 let initial = provider.credentials_async().await?;
402 let provider = CachedProvider::new(provider).with_initial(initial);
403 Ok(Self::Provider(Arc::new(provider)))
404 }
405
406 #[cfg(all(feature = "credentials-sts", feature = "blocking"))]
408 pub fn assume_role_with_provider_blocking(
409 region: Region,
410 role_arn: impl Into<String>,
411 role_session_name: impl Into<String>,
412 source: DynCredentialsProvider,
413 ) -> Result<Self> {
414 Self::assume_role_with_provider_blocking_with_tls_root_store(
415 region,
416 role_arn,
417 role_session_name,
418 source,
419 CredentialsTlsRootStore::BackendDefault,
420 )
421 }
422
423 #[cfg(all(feature = "credentials-sts", feature = "blocking"))]
425 pub fn assume_role_with_provider_blocking_with_tls_root_store(
426 region: Region,
427 role_arn: impl Into<String>,
428 role_session_name: impl Into<String>,
429 source: DynCredentialsProvider,
430 tls_root_store: CredentialsTlsRootStore,
431 ) -> Result<Self> {
432 let provider = StsAssumeRoleProvider {
433 region,
434 role_arn: role_arn.into(),
435 role_session_name: role_session_name.into(),
436 source,
437 tls_root_store,
438 };
439 let initial = provider.credentials_blocking()?;
440 let provider = CachedProvider::new(provider).with_initial(initial);
441 Ok(Self::Provider(Arc::new(provider)))
442 }
443
444 #[cfg(feature = "async")]
445 pub(crate) fn static_credentials(&self) -> Option<&Credentials> {
446 match self {
447 Self::Static(creds) => Some(creds),
448 Self::Anonymous | Self::Provider(_) => None,
449 }
450 }
451
452 #[cfg(feature = "async")]
453 pub(crate) async fn credentials_snapshot_async(&self) -> Result<Option<CredentialsSnapshot>> {
454 match self {
455 Self::Anonymous => Ok(None),
456 Self::Static(creds) => Ok(Some(CredentialsSnapshot::new(creds.clone()))),
457 Self::Provider(provider) => provider.credentials_async().await.map(Some),
458 }
459 }
460
461 #[cfg(feature = "blocking")]
462 pub(crate) fn credentials_snapshot_blocking(&self) -> Result<Option<CredentialsSnapshot>> {
463 match self {
464 Self::Anonymous => Ok(None),
465 Self::Static(creds) => Ok(Some(CredentialsSnapshot::new(creds.clone()))),
466 Self::Provider(provider) => provider.credentials_blocking().map(Some),
467 }
468 }
469}