use crate::{
Authentication, Authenticator,
error::{Error, Result},
introspect::IntrospectionResult,
};
#[derive(Debug, Clone)]
pub enum AuthenticatorEnum {
#[cfg(feature = "kubernetes")]
Kubernetes(crate::kubernetes::KubernetesAuthenticator),
#[cfg(feature = "jwks")]
Jwt(crate::jwks::JWKSWebAuthenticator),
}
#[derive(Debug, Clone)]
pub struct AuthenticatorChain<T>
where
T: Authenticator,
{
authenticators: Vec<T>,
}
impl<T: Authenticator> AuthenticatorChain<T> {
#[must_use]
pub fn builder() -> AuthenticatorChainBuilder<T> {
AuthenticatorChainBuilder {
authenticators: Vec::new(),
}
}
}
impl<T> Authenticator for AuthenticatorChain<T>
where
T: Authenticator,
{
async fn authenticate(
&self,
token: &str,
introspection: &IntrospectionResult,
) -> Result<Authentication> {
for authenticator in &self.authenticators {
if authenticator.can_handle_token(token, introspection) {
return authenticator.authenticate(token, introspection).await;
}
}
Err(Error::NoAuthenticatorCanHandleToken)
}
fn idp_id(&self) -> Option<&String> {
self.authenticators.first().and_then(Authenticator::idp_id)
}
fn idp_ids(&self) -> Vec<Option<&str>> {
self.authenticators
.iter()
.flat_map(Authenticator::idp_ids)
.collect()
}
fn can_handle_token(&self, token: &str, introspection_result: &IntrospectionResult) -> bool {
self.authenticators
.iter()
.any(|authenticator| authenticator.can_handle_token(token, introspection_result))
}
}
#[derive(Debug, Clone)]
pub struct AuthenticatorChainBuilder<T>
where
T: Authenticator,
{
authenticators: Vec<T>,
}
impl<T> AuthenticatorChainBuilder<T>
where
T: Authenticator,
{
#[must_use]
pub fn add_authenticator(mut self, authenticator: impl Into<T>) -> Self {
self.authenticators.push(authenticator.into());
self
}
#[must_use]
pub fn build(self) -> AuthenticatorChain<T> {
AuthenticatorChain {
authenticators: self.authenticators,
}
}
}
#[cfg(any(feature = "kubernetes", feature = "jwks"))]
impl Authenticator for AuthenticatorEnum {
async fn authenticate(
&self,
token: &str,
introspection: &IntrospectionResult,
) -> Result<Authentication> {
match self {
#[cfg(feature = "kubernetes")]
Self::Kubernetes(authenticator) => {
authenticator.authenticate(token, introspection).await
}
#[cfg(feature = "jwks")]
Self::Jwt(authenticator) => authenticator.authenticate(token, introspection).await,
}
}
fn idp_id(&self) -> Option<&String> {
match self {
#[cfg(feature = "kubernetes")]
Self::Kubernetes(authenticator) => authenticator.idp_id(),
#[cfg(feature = "jwks")]
Self::Jwt(authenticator) => authenticator.idp_id(),
}
}
fn can_handle_token(&self, token: &str, introspection_result: &IntrospectionResult) -> bool {
match self {
#[cfg(feature = "kubernetes")]
Self::Kubernetes(authenticator) => {
authenticator.can_handle_token(token, introspection_result)
}
#[cfg(feature = "jwks")]
Self::Jwt(authenticator) => authenticator.can_handle_token(token, introspection_result),
}
}
}
#[cfg(feature = "kubernetes")]
impl From<crate::kubernetes::KubernetesAuthenticator> for AuthenticatorEnum {
fn from(authenticator: crate::kubernetes::KubernetesAuthenticator) -> Self {
Self::Kubernetes(authenticator)
}
}
#[cfg(feature = "jwks")]
impl From<crate::jwks::JWKSWebAuthenticator> for AuthenticatorEnum {
fn from(authenticator: crate::jwks::JWKSWebAuthenticator) -> Self {
Self::Jwt(authenticator)
}
}