1use async_trait::async_trait;
4use http::HeaderMap;
5use thiserror::Error;
6
7#[derive(Error, Debug, serde::Serialize)]
9pub enum AuthError {
10 #[error("Missing API key")]
11 MissingApiKey,
12 #[error("Invalid API key")]
13 InvalidApiKey,
14 #[error("Authentication failed: {0}")]
15 Failed(String),
16}
17
18#[async_trait]
21pub trait AuthenticationProvider: Send + Sync + 'static + std::fmt::Debug {
22 async fn authenticate(
32 &self,
33 headers: &HeaderMap,
34 auth: Option<&serde_json::Value>,
35 ) -> Result<(), AuthError>;
36}
37
38#[derive(Debug, Clone)]
41pub struct DefaultAuthenticationProvider {
42 admin_secret: Option<String>,
44 api_key_name: String,
46}
47
48impl DefaultAuthenticationProvider {
49 pub fn new(admin_secret: Option<String>, api_key_name: Option<String>) -> Self {
56 Self {
57 admin_secret,
58 api_key_name: api_key_name.unwrap_or_else(|| "x-api-key".to_string()),
59 }
60 }
61}
62
63#[async_trait]
64impl AuthenticationProvider for DefaultAuthenticationProvider {
65 async fn authenticate(
66 &self,
67 headers: &HeaderMap,
68 _auth: Option<&serde_json::Value>,
69 ) -> Result<(), AuthError> {
70 let api_key = headers
73 .get(self.api_key_name.as_str())
74 .and_then(|value| value.to_str().ok())
75 .map(|s| s.to_string());
76
77 let api_key = api_key.ok_or(AuthError::MissingApiKey)?;
78
79 if let Some(ref admin_secret) = self.admin_secret {
82 if api_key.as_str() == admin_secret {
83 return Ok(());
84 }
85 }
86
87 Err(AuthError::InvalidApiKey)
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use http::HeaderValue;
97
98 #[tokio::test]
99 async fn test_default_auth_success() {
100 let auth = DefaultAuthenticationProvider::new(Some("secret123".to_string()), None);
101 let mut headers = HeaderMap::new();
102 headers.insert("x-api-key", HeaderValue::from_static("secret123"));
103
104 let result = auth.authenticate(&headers, None).await;
105 assert!(result.is_ok());
106 }
107
108 #[tokio::test]
109 async fn test_default_auth_missing_key() {
110 let auth = DefaultAuthenticationProvider::new(Some("secret123".to_string()), None);
111 let headers = HeaderMap::new();
112
113 let result = auth.authenticate(&headers, None).await;
114 assert!(matches!(result, Err(AuthError::MissingApiKey)));
115 }
116
117 #[tokio::test]
118 async fn test_default_auth_invalid_key() {
119 let auth = DefaultAuthenticationProvider::new(Some("secret123".to_string()), None);
120 let mut headers = HeaderMap::new();
121 headers.insert("x-api-key", HeaderValue::from_static("wrong"));
122
123 let result = auth.authenticate(&headers, None).await;
124 assert!(matches!(result, Err(AuthError::InvalidApiKey)));
125 }
126
127 #[tokio::test]
128 async fn test_default_auth_no_admin_secret() {
129 let auth = DefaultAuthenticationProvider::new(None, None);
130 let mut headers = HeaderMap::new();
131 headers.insert("x-api-key", HeaderValue::from_static("anykey"));
132
133 let result = auth.authenticate(&headers, None).await;
134 assert!(matches!(result, Err(AuthError::InvalidApiKey)));
135 }
136}