use crate::{BoxedFuture, Context, MaybeSend, Result};
use std::fmt::Debug;
use std::future::Future;
use std::ops::Deref;
use std::time::Duration;
pub trait SigningCredential: Clone + Debug + Send + Sync + Unpin + 'static {
fn is_valid(&self) -> bool;
}
impl<T: SigningCredential> SigningCredential for Option<T> {
fn is_valid(&self) -> bool {
let Some(ctx) = self else {
return false;
};
ctx.is_valid()
}
}
pub trait ProvideCredential: Debug + Send + Sync + Unpin + 'static {
type Credential: Send + Sync + Unpin + 'static;
fn provide_credential(
&self,
ctx: &Context,
) -> impl Future<Output = Result<Option<Self::Credential>>> + MaybeSend;
}
pub trait ProvideCredentialDyn: Debug + Send + Sync + Unpin + 'static {
type Credential: Send + Sync + Unpin + 'static;
fn provide_credential_dyn<'a>(
&'a self,
ctx: &'a Context,
) -> BoxedFuture<'a, Result<Option<Self::Credential>>>;
}
impl<T> ProvideCredentialDyn for T
where
T: ProvideCredential + ?Sized,
{
type Credential = T::Credential;
fn provide_credential_dyn<'a>(
&'a self,
ctx: &'a Context,
) -> BoxedFuture<'a, Result<Option<Self::Credential>>> {
Box::pin(self.provide_credential(ctx))
}
}
impl<T> ProvideCredential for std::sync::Arc<T>
where
T: ProvideCredentialDyn + ?Sized,
{
type Credential = T::Credential;
async fn provide_credential(&self, ctx: &Context) -> Result<Option<Self::Credential>> {
self.deref().provide_credential_dyn(ctx).await
}
}
pub trait SignRequest: Debug + Send + Sync + Unpin + 'static {
type Credential: Send + Sync + Unpin + 'static;
fn sign_request<'a>(
&'a self,
ctx: &'a Context,
req: &'a mut http::request::Parts,
credential: Option<&'a Self::Credential>,
expires_in: Option<Duration>,
) -> impl Future<Output = Result<()>> + MaybeSend + 'a;
}
pub trait SignRequestDyn: Debug + Send + Sync + Unpin + 'static {
type Credential: Send + Sync + Unpin + 'static;
fn sign_request_dyn<'a>(
&'a self,
ctx: &'a Context,
req: &'a mut http::request::Parts,
credential: Option<&'a Self::Credential>,
expires_in: Option<Duration>,
) -> BoxedFuture<'a, Result<()>>;
}
impl<T> SignRequestDyn for T
where
T: SignRequest + ?Sized,
{
type Credential = T::Credential;
fn sign_request_dyn<'a>(
&'a self,
ctx: &'a Context,
req: &'a mut http::request::Parts,
credential: Option<&'a Self::Credential>,
expires_in: Option<Duration>,
) -> BoxedFuture<'a, Result<()>> {
Box::pin(self.sign_request(ctx, req, credential, expires_in))
}
}
impl<T> SignRequest for std::sync::Arc<T>
where
T: SignRequestDyn + ?Sized,
{
type Credential = T::Credential;
async fn sign_request(
&self,
ctx: &Context,
req: &mut http::request::Parts,
credential: Option<&Self::Credential>,
expires_in: Option<Duration>,
) -> Result<()> {
self.deref()
.sign_request_dyn(ctx, req, credential, expires_in)
.await
}
}
pub struct ProvideCredentialChain<C> {
providers: Vec<Box<dyn ProvideCredentialDyn<Credential = C>>>,
}
impl<C> ProvideCredentialChain<C>
where
C: Send + Sync + Unpin + 'static,
{
pub fn new() -> Self {
Self {
providers: Vec::new(),
}
}
pub fn push(mut self, provider: impl ProvideCredential<Credential = C> + 'static) -> Self {
self.providers.push(Box::new(provider));
self
}
pub fn push_front(
mut self,
provider: impl ProvideCredential<Credential = C> + 'static,
) -> Self {
self.providers.insert(0, Box::new(provider));
self
}
pub fn from_vec(providers: Vec<Box<dyn ProvideCredentialDyn<Credential = C>>>) -> Self {
Self { providers }
}
pub fn len(&self) -> usize {
self.providers.len()
}
pub fn is_empty(&self) -> bool {
self.providers.is_empty()
}
}
impl<C> Default for ProvideCredentialChain<C>
where
C: Send + Sync + Unpin + 'static,
{
fn default() -> Self {
Self::new()
}
}
impl<C> Debug for ProvideCredentialChain<C>
where
C: Send + Sync + Unpin + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProvideCredentialChain")
.field("providers_count", &self.providers.len())
.finish()
}
}
impl<C> ProvideCredential for ProvideCredentialChain<C>
where
C: Send + Sync + Unpin + 'static,
{
type Credential = C;
async fn provide_credential(&self, ctx: &Context) -> Result<Option<Self::Credential>> {
for provider in &self.providers {
log::debug!("Trying credential provider: {provider:?}");
match provider.provide_credential_dyn(ctx).await {
Ok(Some(cred)) => {
log::debug!("Successfully loaded credential from provider: {provider:?}");
return Ok(Some(cred));
}
Ok(None) => {
log::debug!("No credential found in provider: {provider:?}");
continue;
}
Err(e) => {
log::warn!("Error loading credential from provider {provider:?}: {e:?}");
continue;
}
}
}
Ok(None)
}
}