1use crate::{PluginCapabilities, PluginContext, PluginResult, Result};
8use axum::http::{HeaderMap, Method, Uri};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12#[async_trait::async_trait]
18pub trait AuthPlugin: Send + Sync {
19 fn capabilities(&self) -> PluginCapabilities;
21
22 async fn initialize(&self, config: &AuthPluginConfig) -> Result<()>;
24
25 async fn authenticate(
39 &self,
40 context: &PluginContext,
41 request: &AuthRequest,
42 config: &AuthPluginConfig,
43 ) -> Result<PluginResult<AuthResponse>>;
44
45 fn validate_config(&self, config: &AuthPluginConfig) -> Result<()>;
47
48 fn supported_schemes(&self) -> Vec<String>;
50
51 async fn cleanup(&self) -> Result<()>;
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct AuthPluginConfig {
58 pub config: HashMap<String, serde_json::Value>,
60 pub enabled: bool,
62 pub priority: i32,
64 pub settings: HashMap<String, serde_json::Value>,
66}
67
68impl Default for AuthPluginConfig {
69 fn default() -> Self {
70 Self {
71 config: HashMap::new(),
72 enabled: true,
73 priority: 100,
74 settings: HashMap::new(),
75 }
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct AuthRequest {
82 pub method: Method,
84 pub uri: Uri,
86 pub headers: HeaderMap,
88 pub body: Option<Vec<u8>>,
90 pub query_params: HashMap<String, String>,
92 pub client_ip: Option<String>,
94 pub user_agent: Option<String>,
96 pub timestamp: chrono::DateTime<chrono::Utc>,
98}
99
100impl AuthRequest {
101 pub fn from_axum(method: Method, uri: Uri, headers: HeaderMap, body: Option<Vec<u8>>) -> Self {
103 let query_params = uri
104 .query()
105 .map(|q| url::form_urlencoded::parse(q.as_bytes()).into_owned().collect())
106 .unwrap_or_default();
107
108 let client_ip = headers
109 .get("x-forwarded-for")
110 .or_else(|| headers.get("x-real-ip"))
111 .and_then(|h| h.to_str().ok())
112 .map(|s| s.to_string());
113
114 let user_agent =
115 headers.get("user-agent").and_then(|h| h.to_str().ok()).map(|s| s.to_string());
116
117 Self {
118 method,
119 uri,
120 headers,
121 body,
122 query_params,
123 client_ip,
124 user_agent,
125 timestamp: chrono::Utc::now(),
126 }
127 }
128
129 pub fn authorization_header(&self) -> Option<&str> {
131 self.headers.get("authorization").and_then(|h| h.to_str().ok())
132 }
133
134 pub fn bearer_token(&self) -> Option<&str> {
136 self.authorization_header().and_then(|auth| auth.strip_prefix("Bearer "))
137 }
138
139 pub fn basic_credentials(&self) -> Option<(String, String)> {
141 self.authorization_header()
142 .and_then(|auth| auth.strip_prefix("Basic "))
143 .and_then(|encoded| {
144 base64::Engine::decode(&base64::engine::general_purpose::STANDARD, encoded).ok()
145 })
146 .and_then(|decoded| String::from_utf8(decoded).ok())
147 .and_then(|creds| {
148 let parts: Vec<&str> = creds.splitn(2, ':').collect();
149 if parts.len() == 2 {
150 Some((parts[0].to_string(), parts[1].to_string()))
151 } else {
152 None
153 }
154 })
155 }
156
157 pub fn header(&self, name: &str) -> Option<&str> {
159 self.headers.get(name).and_then(|h| h.to_str().ok())
160 }
161
162 pub fn query_param(&self, name: &str) -> Option<&str> {
164 self.query_params.get(name).map(|s| s.as_str())
165 }
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct AuthResponse {
171 pub authenticated: bool,
173 pub identity: Option<UserIdentity>,
175 pub claims: HashMap<String, serde_json::Value>,
177 pub metadata: HashMap<String, serde_json::Value>,
179 pub error_message: Option<String>,
181}
182
183impl AuthResponse {
184 pub fn success(identity: UserIdentity, claims: HashMap<String, serde_json::Value>) -> Self {
186 Self {
187 authenticated: true,
188 identity: Some(identity),
189 claims,
190 metadata: HashMap::new(),
191 error_message: None,
192 }
193 }
194
195 pub fn failure<S: Into<String>>(error_message: S) -> Self {
197 Self {
198 authenticated: false,
199 identity: None,
200 claims: HashMap::new(),
201 metadata: HashMap::new(),
202 error_message: Some(error_message.into()),
203 }
204 }
205
206 pub fn with_metadata<S: Into<String>>(mut self, key: S, value: serde_json::Value) -> Self {
208 self.metadata.insert(key.into(), value);
209 self
210 }
211
212 pub fn is_authenticated(&self) -> bool {
214 self.authenticated
215 }
216
217 pub fn identity(&self) -> Option<&UserIdentity> {
219 self.identity.as_ref()
220 }
221
222 pub fn claims(&self) -> &HashMap<String, serde_json::Value> {
224 &self.claims
225 }
226
227 pub fn error_message(&self) -> Option<&str> {
229 self.error_message.as_deref()
230 }
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct UserIdentity {
236 pub user_id: String,
238 pub username: Option<String>,
240 pub email: Option<String>,
242 pub display_name: Option<String>,
244 pub roles: Vec<String>,
246 pub groups: Vec<String>,
248 pub attributes: HashMap<String, serde_json::Value>,
250}
251
252impl UserIdentity {
253 pub fn new<S: Into<String>>(user_id: S) -> Self {
255 Self {
256 user_id: user_id.into(),
257 username: None,
258 email: None,
259 display_name: None,
260 roles: Vec::new(),
261 groups: Vec::new(),
262 attributes: HashMap::new(),
263 }
264 }
265
266 pub fn with_username<S: Into<String>>(mut self, username: S) -> Self {
268 self.username = Some(username.into());
269 self
270 }
271
272 pub fn with_email<S: Into<String>>(mut self, email: S) -> Self {
274 self.email = Some(email.into());
275 self
276 }
277
278 pub fn with_display_name<S: Into<String>>(mut self, display_name: S) -> Self {
280 self.display_name = Some(display_name.into());
281 self
282 }
283
284 pub fn with_role<S: Into<String>>(mut self, role: S) -> Self {
286 self.roles.push(role.into());
287 self
288 }
289
290 pub fn with_roles(mut self, roles: Vec<String>) -> Self {
292 self.roles.extend(roles);
293 self
294 }
295
296 pub fn with_group<S: Into<String>>(mut self, group: S) -> Self {
298 self.groups.push(group.into());
299 self
300 }
301
302 pub fn with_attribute<S: Into<String>>(mut self, key: S, value: serde_json::Value) -> Self {
304 self.attributes.insert(key.into(), value);
305 self
306 }
307
308 pub fn has_role(&self, role: &str) -> bool {
310 self.roles.iter().any(|r| r == role)
311 }
312
313 pub fn in_group(&self, group: &str) -> bool {
315 self.groups.iter().any(|g| g == group)
316 }
317}
318
319pub struct AuthPluginEntry {
321 pub plugin_id: crate::PluginId,
323 pub plugin: Box<dyn AuthPlugin>,
325 pub config: AuthPluginConfig,
327 pub capabilities: PluginCapabilities,
329}
330
331impl AuthPluginEntry {
332 pub fn new(
334 plugin_id: crate::PluginId,
335 plugin: Box<dyn AuthPlugin>,
336 config: AuthPluginConfig,
337 ) -> Self {
338 let capabilities = plugin.capabilities();
339 Self {
340 plugin_id,
341 plugin,
342 config,
343 capabilities,
344 }
345 }
346
347 pub fn is_enabled(&self) -> bool {
349 self.config.enabled
350 }
351
352 pub fn priority(&self) -> i32 {
354 self.config.priority
355 }
356}
357
358pub trait AuthPluginFactory: Send + Sync {
360 fn create_plugin(&self) -> Result<Box<dyn AuthPlugin>>;
362}
363
364#[cfg(test)]
365mod tests {
366
367 #[test]
368 fn test_module_compiles() {
369 }
371}