use std::sync::Arc;
use crate::runtime::error::Error;
/// Trait for authenticating requests.
pub trait Authenticator<R: aioduct::RuntimePoll, C: aioduct::ConnectorSend>:
Send + Sync + std::fmt::Debug
{
/// Apply authentication to the request builder.
fn authenticate<'a>(
&self,
req: aioduct::RequestBuilderSend<'a, R, C>,
) -> Result<aioduct::RequestBuilderSend<'a, R, C>, Error>;
}
/// Bearer token authentication.
///
/// Use [`BearerAuth::new`] with a static token, or
/// [`BearerAuth::from_provider`] with a function that returns the
/// current token (evaluated on every request).
pub struct BearerAuth {
token: TokenSource,
}
enum TokenSource {
Static(String),
Dynamic(Arc<dyn Fn() -> String + Send + Sync>),
}
impl std::fmt::Debug for TokenSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Static(t) => f.debug_tuple("Static").field(&t).finish(),
Self::Dynamic(_) => f.debug_tuple("Dynamic").field(&"<function>").finish(),
}
}
}
impl Clone for TokenSource {
fn clone(&self) -> Self {
match self {
Self::Static(t) => Self::Static(t.clone()),
Self::Dynamic(f) => Self::Dynamic(Arc::clone(f)),
}
}
}
impl std::fmt::Debug for BearerAuth {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BearerAuth").field("token", &self.token).finish()
}
}
impl Clone for BearerAuth {
fn clone(&self) -> Self {
Self {
token: self.token.clone(),
}
}
}
impl BearerAuth {
/// Create a new bearer token authenticator with a static token.
pub fn new(token: impl Into<String>) -> Self {
Self {
token: TokenSource::Static(token.into()),
}
}
/// Create a bearer token authenticator that evaluates the given
/// function on every request to obtain the current token.
pub fn from_provider(f: impl Fn() -> String + Send + Sync + 'static) -> Self {
Self {
token: TokenSource::Dynamic(Arc::new(f)),
}
}
}
impl<R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> Authenticator<R, C> for BearerAuth {
fn authenticate<'a>(
&self,
req: aioduct::RequestBuilderSend<'a, R, C>,
) -> Result<aioduct::RequestBuilderSend<'a, R, C>, Error> {
let token = match &self.token {
TokenSource::Static(t) => t.clone(),
TokenSource::Dynamic(f) => f(),
};
Ok(req.bearer_auth(&token))
}
}
/// API key authentication.
///
/// Use [`ApiKeyAuth::new`] with a static key, or
/// [`ApiKeyAuth::from_provider`] with a function that returns the
/// current key (evaluated on every request).
pub struct ApiKeyAuth {
header_name: String,
api_key: KeySource,
}
enum KeySource {
Static(String),
Dynamic(Arc<dyn Fn() -> String + Send + Sync>),
}
impl std::fmt::Debug for KeySource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Static(k) => f.debug_tuple("Static").field(&k).finish(),
Self::Dynamic(_) => f.debug_tuple("Dynamic").field(&"<function>").finish(),
}
}
}
impl Clone for KeySource {
fn clone(&self) -> Self {
match self {
Self::Static(k) => Self::Static(k.clone()),
Self::Dynamic(f) => Self::Dynamic(Arc::clone(f)),
}
}
}
impl std::fmt::Debug for ApiKeyAuth {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ApiKeyAuth")
.field("header_name", &self.header_name)
.field("api_key", &self.api_key)
.finish()
}
}
impl Clone for ApiKeyAuth {
fn clone(&self) -> Self {
Self {
header_name: self.header_name.clone(),
api_key: self.api_key.clone(),
}
}
}
impl ApiKeyAuth {
/// Create a new API key authenticator with a static key.
pub fn new(header_name: impl Into<String>, api_key: impl Into<String>) -> Self {
Self {
header_name: header_name.into(),
api_key: KeySource::Static(api_key.into()),
}
}
/// Create an API key authenticator that evaluates the given
/// function on every request to obtain the current key.
pub fn from_provider(
header_name: impl Into<String>,
f: impl Fn() -> String + Send + Sync + 'static,
) -> Self {
Self {
header_name: header_name.into(),
api_key: KeySource::Dynamic(Arc::new(f)),
}
}
}
impl<R: aioduct::RuntimePoll, C: aioduct::ConnectorSend> Authenticator<R, C> for ApiKeyAuth {
fn authenticate<'a>(
&self,
req: aioduct::RequestBuilderSend<'a, R, C>,
) -> Result<aioduct::RequestBuilderSend<'a, R, C>, Error> {
let key = match &self.api_key {
KeySource::Static(k) => k.clone(),
KeySource::Dynamic(f) => f(),
};
Ok(req.header_str(&self.header_name, &key)?)
}
}