mod layer;
pub mod rate_limit;
mod service;
pub use layer::AuthLayer;
pub use rate_limit::{
IpKeyExtractor, KeyExtractor, RateLimitLayer, RateLimitRejection, RateLimitService,
};
pub use service::{AuthService, AuthServiceFuture};
#[derive(Debug, Clone)]
pub struct AuthLayerConfig {
pub allow_anonymous: bool,
pub bypass_methods: Vec<String>,
pub auth_header: String,
pub api_key_header: String,
}
impl Default for AuthLayerConfig {
fn default() -> Self {
Self {
allow_anonymous: false,
bypass_methods: vec!["initialize".to_string(), "ping".to_string()],
auth_header: "Authorization".to_string(),
api_key_header: "X-API-Key".to_string(),
}
}
}
impl AuthLayerConfig {
#[must_use]
pub fn allow_anonymous() -> Self {
Self {
allow_anonymous: true,
..Default::default()
}
}
#[must_use]
pub fn with_bypass_methods(methods: Vec<String>) -> Self {
Self {
bypass_methods: methods,
..Default::default()
}
}
#[must_use]
pub fn bypass_method(mut self, method: impl Into<String>) -> Self {
self.bypass_methods.push(method.into());
self
}
#[must_use]
pub fn auth_header(mut self, header: impl Into<String>) -> Self {
self.auth_header = header.into();
self
}
#[must_use]
pub fn api_key_header(mut self, header: impl Into<String>) -> Self {
self.api_key_header = header.into();
self
}
#[must_use]
pub fn should_bypass(&self, method: &str) -> bool {
self.bypass_methods.iter().any(|m| m == method)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = AuthLayerConfig::default();
assert!(!config.allow_anonymous);
assert!(config.bypass_methods.contains(&"initialize".to_string()));
assert!(config.bypass_methods.contains(&"ping".to_string()));
assert_eq!(config.auth_header, "Authorization");
assert_eq!(config.api_key_header, "X-API-Key");
}
#[test]
fn test_allow_anonymous() {
let config = AuthLayerConfig::allow_anonymous();
assert!(config.allow_anonymous);
}
#[test]
fn test_should_bypass() {
let config = AuthLayerConfig::default();
assert!(config.should_bypass("initialize"));
assert!(config.should_bypass("ping"));
assert!(!config.should_bypass("tools/call"));
}
#[test]
fn test_custom_bypass_methods() {
let config =
AuthLayerConfig::with_bypass_methods(vec!["health".to_string()]).bypass_method("ready");
assert!(config.should_bypass("health"));
assert!(config.should_bypass("ready"));
assert!(!config.should_bypass("initialize")); }
#[test]
fn test_custom_headers() {
let config = AuthLayerConfig::default()
.auth_header("X-Auth-Token")
.api_key_header("X-Custom-Key");
assert_eq!(config.auth_header, "X-Auth-Token");
assert_eq!(config.api_key_header, "X-Custom-Key");
}
}