use std::any::TypeId;
use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use crate::secrets::Secret;
pub trait CredentialTarget: Send + Sync + 'static {}
#[async_trait]
pub trait ProvideBearerToken<Target: CredentialTarget>: Send + Sync + 'static {
async fn resolve_bearer_token(&self, target: &Target) -> Result<Secret<String>, String>;
}
#[async_trait]
pub trait ProvideApiKey<Target: CredentialTarget>: Send + Sync + 'static {
async fn resolve_api_key(&self, target: &Target) -> Result<Secret<String>, String>;
}
#[async_trait]
pub trait ProvideBasicAuth<Target: CredentialTarget>: Send + Sync + 'static {
async fn resolve_basic_auth(&self, target: &Target)
-> Result<(String, Secret<String>), String>;
}
#[async_trait]
trait ErasedBearerResolver: Send + Sync + 'static {
async fn resolve_erased(
&self,
target: &(dyn std::any::Any + Send + Sync),
) -> Result<Secret<String>, String>;
}
struct TypedBearerResolver<Target, P> {
provider: Arc<P>,
_marker: std::marker::PhantomData<Target>,
}
#[async_trait]
impl<Target: CredentialTarget, P: ProvideBearerToken<Target>> ErasedBearerResolver
for TypedBearerResolver<Target, P>
{
async fn resolve_erased(
&self,
target: &(dyn std::any::Any + Send + Sync),
) -> Result<Secret<String>, String> {
let typed_target = target.downcast_ref::<Target>().ok_or_else(|| {
format!(
"Invalid target type: expected {}",
std::any::type_name::<Target>()
)
})?;
self.provider.resolve_bearer_token(typed_target).await
}
}
#[async_trait]
trait ErasedApiKeyResolver: Send + Sync + 'static {
async fn resolve_erased(
&self,
target: &(dyn std::any::Any + Send + Sync),
) -> Result<Secret<String>, String>;
}
struct TypedApiKeyResolver<Target, P> {
provider: Arc<P>,
_marker: std::marker::PhantomData<Target>,
}
#[async_trait]
impl<Target: CredentialTarget, P: ProvideApiKey<Target>> ErasedApiKeyResolver
for TypedApiKeyResolver<Target, P>
{
async fn resolve_erased(
&self,
target: &(dyn std::any::Any + Send + Sync),
) -> Result<Secret<String>, String> {
let typed_target = target.downcast_ref::<Target>().ok_or_else(|| {
format!(
"Invalid target type: expected {}",
std::any::type_name::<Target>()
)
})?;
self.provider.resolve_api_key(typed_target).await
}
}
#[async_trait]
trait ErasedBasicAuthResolver: Send + Sync + 'static {
async fn resolve_erased(
&self,
target: &(dyn std::any::Any + Send + Sync),
) -> Result<(String, Secret<String>), String>;
}
struct TypedBasicAuthResolver<Target, P> {
provider: Arc<P>,
_marker: std::marker::PhantomData<Target>,
}
#[async_trait]
impl<Target: CredentialTarget, P: ProvideBasicAuth<Target>> ErasedBasicAuthResolver
for TypedBasicAuthResolver<Target, P>
{
async fn resolve_erased(
&self,
target: &(dyn std::any::Any + Send + Sync),
) -> Result<(String, Secret<String>), String> {
let typed_target = target.downcast_ref::<Target>().ok_or_else(|| {
format!(
"Invalid target type: expected {}",
std::any::type_name::<Target>()
)
})?;
self.provider.resolve_basic_auth(typed_target).await
}
}
#[derive(Default)]
pub struct CredentialsBuilder {
bearer_resolvers: HashMap<TypeId, Arc<dyn ErasedBearerResolver>>,
api_key_resolvers: HashMap<TypeId, Arc<dyn ErasedApiKeyResolver>>,
basic_auth_resolvers: HashMap<TypeId, Arc<dyn ErasedBasicAuthResolver>>,
}
impl CredentialsBuilder {
pub fn register_bearer<Target: CredentialTarget, P: ProvideBearerToken<Target>>(
&mut self,
provider: Arc<P>,
) -> Result<(), String> {
let type_id = TypeId::of::<Target>();
if self.bearer_resolvers.contains_key(&type_id) {
return Err(format!(
"Credentials resolver already registered for target: {}",
std::any::type_name::<Target>()
));
}
let resolver = TypedBearerResolver {
provider,
_marker: std::marker::PhantomData,
};
self.bearer_resolvers.insert(type_id, Arc::new(resolver));
Ok(())
}
pub fn register_api_key<Target: CredentialTarget, P: ProvideApiKey<Target>>(
&mut self,
provider: Arc<P>,
) -> Result<(), String> {
let type_id = TypeId::of::<Target>();
if self.api_key_resolvers.contains_key(&type_id) {
return Err(format!(
"Credentials resolver already registered for target: {}",
std::any::type_name::<Target>()
));
}
let resolver = TypedApiKeyResolver {
provider,
_marker: std::marker::PhantomData,
};
self.api_key_resolvers.insert(type_id, Arc::new(resolver));
Ok(())
}
pub fn register_basic_auth<Target: CredentialTarget, P: ProvideBasicAuth<Target>>(
&mut self,
provider: Arc<P>,
) -> Result<(), String> {
let type_id = TypeId::of::<Target>();
if self.basic_auth_resolvers.contains_key(&type_id) {
return Err(format!(
"Credentials resolver already registered for target: {}",
std::any::type_name::<Target>()
));
}
let resolver = TypedBasicAuthResolver {
provider,
_marker: std::marker::PhantomData,
};
self.basic_auth_resolvers
.insert(type_id, Arc::new(resolver));
Ok(())
}
pub fn build(self) -> CredentialsHandle {
CredentialsHandle {
bearer_resolvers: Arc::new(self.bearer_resolvers),
api_key_resolvers: Arc::new(self.api_key_resolvers),
basic_auth_resolvers: Arc::new(self.basic_auth_resolvers),
}
}
}
#[derive(Clone, Default)]
pub struct CredentialsHandle {
bearer_resolvers: Arc<HashMap<TypeId, Arc<dyn ErasedBearerResolver>>>,
api_key_resolvers: Arc<HashMap<TypeId, Arc<dyn ErasedApiKeyResolver>>>,
basic_auth_resolvers: Arc<HashMap<TypeId, Arc<dyn ErasedBasicAuthResolver>>>,
}
impl CredentialsHandle {
pub async fn resolve_bearer_token<Target: CredentialTarget>(
&self,
target: &Target,
) -> Result<Secret<String>, String> {
let type_id = TypeId::of::<Target>();
if let Some(resolver) = self.bearer_resolvers.get(&type_id) {
resolver.resolve_erased(target).await
} else {
Err(format!(
"No registered credentials provider implements ProvideBearerToken<{}>",
std::any::type_name::<Target>()
))
}
}
pub async fn resolve_api_key<Target: CredentialTarget>(
&self,
target: &Target,
) -> Result<Secret<String>, String> {
let type_id = TypeId::of::<Target>();
if let Some(resolver) = self.api_key_resolvers.get(&type_id) {
resolver.resolve_erased(target).await
} else {
Err(format!(
"No registered credentials provider implements ProvideApiKey<{}>",
std::any::type_name::<Target>()
))
}
}
pub async fn resolve_basic_auth<Target: CredentialTarget>(
&self,
target: &Target,
) -> Result<(String, Secret<String>), String> {
let type_id = TypeId::of::<Target>();
if let Some(resolver) = self.basic_auth_resolvers.get(&type_id) {
resolver.resolve_erased(target).await
} else {
Err(format!(
"No registered credentials provider implements ProvideBasicAuth<{}>",
std::any::type_name::<Target>()
))
}
}
}
pub trait PluggableCredentialsProvider: Send + Sync + 'static {
type Config: serde::de::DeserializeOwned + Default + Send + Sync;
fn register_provider(
config: Self::Config,
builder: &mut CredentialsBuilder,
) -> Result<(), String>;
}