pub mod jwt;
pub mod keto;
#[cfg(feature = "keto")]
pub mod keto_proto;
pub use jwt::{JwtClaims, JwtLayer, JwtValidator, extract_jwt_claims};
pub use keto::{KetoClient, KetoConfig, KetoLayer, Permission, extract_subject};
#[derive(Debug, Clone)]
pub enum AuthStrategy {
Jwt(JwtValidator),
Keto(KetoClient),
None,
}
impl Default for AuthStrategy {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Clone)]
pub struct AuthContext {
pub subject: Option<String>,
pub claims: Option<jwt::JwtClaims>,
pub is_authenticated: bool,
}
impl AuthContext {
pub fn unauthenticated() -> Self {
Self {
subject: None,
claims: None,
is_authenticated: false,
}
}
pub fn authenticated(subject: impl Into<String>, claims: Option<jwt::JwtClaims>) -> Self {
Self {
subject: Some(subject.into()),
claims,
is_authenticated: true,
}
}
pub fn is_authenticated(&self) -> bool {
self.is_authenticated
}
pub fn subject(&self) -> Option<&String> {
self.subject.as_ref()
}
}
impl Default for AuthContext {
fn default() -> Self {
Self::unauthenticated()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct AuthLayer {
strategy: AuthStrategy,
}
impl AuthLayer {
pub fn new(strategy: AuthStrategy) -> Self {
Self { strategy }
}
pub fn jwt(validator: jwt::JwtValidator) -> Self {
Self::new(AuthStrategy::Jwt(validator))
}
pub fn keto(client: keto::KetoClient) -> Self {
Self::new(AuthStrategy::Keto(client))
}
pub fn none() -> Self {
Self::new(AuthStrategy::None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auth_context_unauthenticated() {
let ctx = AuthContext::unauthenticated();
assert!(!ctx.is_authenticated());
assert!(ctx.subject().is_none());
}
#[test]
fn test_auth_context_authenticated() {
let ctx = AuthContext::authenticated("user-1", None);
assert!(ctx.is_authenticated());
assert_eq!(ctx.subject(), Some(&"user-1".to_string()));
}
#[test]
fn test_auth_layer_new() {
let layer = AuthLayer::none();
match layer.strategy {
AuthStrategy::None => {}
_ => panic!("Expected None strategy"),
}
}
}