use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use crate::context::SecurityContext;
use crate::internal_auth::{InternalAuthNError, InternalAuthenticator, PlatformIdentity};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum AuthNError {
#[error("invalid or expired token")]
InvalidToken,
#[error("authentication backend unavailable")]
Unavailable,
#[error("authentication failed: {0}")]
Other(String),
}
pub trait BearerAuthenticator: Send + Sync {
fn authenticate(
&self,
token: &str,
) -> impl Future<Output = Result<SecurityContext, AuthNError>> + Send;
}
type BearerFuture<'a> =
Pin<Box<dyn Future<Output = Result<SecurityContext, AuthNError>> + Send + 'a>>;
trait ErasedBearer: Send + Sync {
fn authenticate<'a>(&'a self, token: &'a str) -> BearerFuture<'a>;
}
impl<A: BearerAuthenticator> ErasedBearer for A {
fn authenticate<'a>(&'a self, token: &'a str) -> BearerFuture<'a> {
Box::pin(BearerAuthenticator::authenticate(self, token))
}
}
#[derive(Clone)]
pub struct DynBearerAuthenticator(Arc<dyn ErasedBearer>);
impl DynBearerAuthenticator {
#[must_use]
pub fn new<A: BearerAuthenticator + 'static>(authenticator: A) -> Self {
Self(Arc::new(authenticator))
}
#[must_use]
pub fn from_arc<A: BearerAuthenticator + 'static>(authenticator: Arc<A>) -> Self {
Self(authenticator)
}
}
impl std::fmt::Debug for DynBearerAuthenticator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynBearerAuthenticator")
.finish_non_exhaustive()
}
}
impl BearerAuthenticator for DynBearerAuthenticator {
async fn authenticate(&self, token: &str) -> Result<SecurityContext, AuthNError> {
self.0.authenticate(token).await
}
}
type InternalFuture<'a> =
Pin<Box<dyn Future<Output = Result<PlatformIdentity, InternalAuthNError>> + Send + 'a>>;
trait ErasedInternal: Send + Sync {
fn authenticate<'a>(&'a self, token: &'a str) -> InternalFuture<'a>;
}
impl<A: InternalAuthenticator> ErasedInternal for A {
fn authenticate<'a>(&'a self, token: &'a str) -> InternalFuture<'a> {
Box::pin(InternalAuthenticator::authenticate(self, token))
}
}
#[derive(Clone)]
pub struct DynInternalAuthenticator(Arc<dyn ErasedInternal>);
impl DynInternalAuthenticator {
#[must_use]
pub fn new<A: InternalAuthenticator + 'static>(authenticator: A) -> Self {
Self(Arc::new(authenticator))
}
#[must_use]
pub fn from_arc<A: InternalAuthenticator + 'static>(authenticator: Arc<A>) -> Self {
Self(authenticator)
}
}
impl std::fmt::Debug for DynInternalAuthenticator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynInternalAuthenticator")
.finish_non_exhaustive()
}
}
impl InternalAuthenticator for DynInternalAuthenticator {
async fn authenticate(&self, token: &str) -> Result<PlatformIdentity, InternalAuthNError> {
self.0.authenticate(token).await
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use uuid::Uuid;
struct FakeBearer;
impl BearerAuthenticator for FakeBearer {
async fn authenticate(&self, token: &str) -> Result<SecurityContext, AuthNError> {
if token == "bad" {
return Err(AuthNError::InvalidToken);
}
SecurityContext::builder()
.subject_id(Uuid::from_u128(1))
.subject_tenant_id(Uuid::from_u128(2))
.subject_type(token)
.build()
.map_err(|e| AuthNError::Other(e.to_string()))
}
}
struct FakeInternal;
impl InternalAuthenticator for FakeInternal {
async fn authenticate(&self, token: &str) -> Result<PlatformIdentity, InternalAuthNError> {
if token == "bad" {
return Err(InternalAuthNError::InvalidToken);
}
Ok(PlatformIdentity::Shared {
name: token.to_owned(),
})
}
}
#[tokio::test]
async fn dyn_bearer_new_delegates_to_inner() {
let auth = DynBearerAuthenticator::new(FakeBearer);
let ctx = BearerAuthenticator::authenticate(&auth, "alice")
.await
.expect("authenticates");
assert_eq!(ctx.subject_type(), Some("alice"));
let err = BearerAuthenticator::authenticate(&auth, "bad")
.await
.expect_err("rejects");
assert!(matches!(err, AuthNError::InvalidToken));
}
#[tokio::test]
async fn dyn_bearer_from_arc_and_clone_delegate_to_inner() {
let auth = DynBearerAuthenticator::from_arc(Arc::new(FakeBearer));
let cloned = auth.clone();
let ctx = BearerAuthenticator::authenticate(&cloned, "bob")
.await
.expect("authenticates");
assert_eq!(ctx.subject_type(), Some("bob"));
}
#[test]
fn dyn_bearer_debug_is_non_exhaustive() {
let auth = DynBearerAuthenticator::new(FakeBearer);
assert_eq!(format!("{auth:?}"), "DynBearerAuthenticator { .. }");
}
#[tokio::test]
async fn dyn_internal_new_delegates_to_inner() {
let auth = DynInternalAuthenticator::new(FakeInternal);
let identity = InternalAuthenticator::authenticate(&auth, "gear-a")
.await
.expect("authenticates");
assert_eq!(identity.peer_name(), "gear-a");
let err = InternalAuthenticator::authenticate(&auth, "bad")
.await
.expect_err("rejects");
assert!(matches!(err, InternalAuthNError::InvalidToken));
}
#[tokio::test]
async fn dyn_internal_from_arc_and_clone_delegate_to_inner() {
let auth = DynInternalAuthenticator::from_arc(Arc::new(FakeInternal));
let cloned = auth.clone();
let identity = InternalAuthenticator::authenticate(&cloned, "gear-b")
.await
.expect("authenticates");
assert_eq!(identity.peer_name(), "gear-b");
}
#[test]
fn dyn_internal_debug_is_non_exhaustive() {
let auth = DynInternalAuthenticator::new(FakeInternal);
assert_eq!(format!("{auth:?}"), "DynInternalAuthenticator { .. }");
}
}