use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait;
use futures::future::BoxFuture;
use reqwest::Client;
use crate::service::HttpService;
use crate::{Result, RetryConfig, TemporaryToken, TokenCache};
#[async_trait]
pub trait CredentialProvider: std::fmt::Debug + Send + Sync {
type Credential;
async fn get_credential(&self) -> Result<Arc<Self::Credential>>;
}
#[derive(Debug)]
pub struct StaticCredentialProvider<T> {
credential: Arc<T>,
}
impl<T> StaticCredentialProvider<T> {
pub fn new(credential: T) -> Self {
Self {
credential: Arc::new(credential),
}
}
}
#[async_trait]
impl<T> CredentialProvider for StaticCredentialProvider<T>
where
T: std::fmt::Debug + Send + Sync,
{
type Credential = T;
async fn get_credential(&self) -> Result<Arc<T>> {
Ok(Arc::clone(&self.credential))
}
}
#[derive(Debug)]
pub(crate) struct TokenCredentialProvider<T: TokenProvider> {
inner: T,
client: Client,
service: Arc<dyn HttpService>,
retry: RetryConfig,
cache: TokenCache<Arc<T::Credential>>,
}
impl<T: TokenProvider> TokenCredentialProvider<T> {
pub(crate) fn new(
inner: T,
client: Client,
service: Arc<dyn HttpService>,
retry: RetryConfig,
) -> Self {
Self {
inner,
client,
service,
retry,
cache: Default::default(),
}
}
pub(crate) fn with_min_ttl(mut self, min_ttl: Duration) -> Self {
self.cache = self.cache.with_min_ttl(min_ttl);
self
}
}
#[async_trait]
impl<T: TokenProvider> CredentialProvider for TokenCredentialProvider<T> {
type Credential = T::Credential;
async fn get_credential(&self) -> Result<Arc<Self::Credential>> {
self.cache
.get_or_insert_with(|| {
self.inner
.fetch_token(&self.client, &self.service, &self.retry)
})
.await
}
}
pub trait RequestSigner: Debug + Send + Sync {
fn sign<'a>(
&'a self,
req: reqwest::RequestBuilder,
) -> BoxFuture<'a, Result<reqwest::RequestBuilder>>;
}
#[derive(Debug)]
pub struct SignerChain {
signers: Vec<Arc<dyn RequestSigner>>,
}
impl SignerChain {
pub fn new(signers: Vec<Arc<dyn RequestSigner>>) -> Self {
Self { signers }
}
}
impl RequestSigner for SignerChain {
fn sign<'a>(
&'a self,
req: reqwest::RequestBuilder,
) -> BoxFuture<'a, Result<reqwest::RequestBuilder>> {
Box::pin(async move {
let mut current = req;
for signer in &self.signers {
current = signer.sign(current).await?;
}
Ok(current)
})
}
}
#[async_trait]
pub(crate) trait TokenProvider: std::fmt::Debug + Send + Sync {
type Credential: std::fmt::Debug + Send + Sync;
async fn fetch_token(
&self,
client: &Client,
service: &Arc<dyn HttpService>,
retry: &RetryConfig,
) -> Result<TemporaryToken<Arc<Self::Credential>>>;
}