litellm_rs/server/routes/ai/
context.rs1use crate::core::models::ApiKey;
4use crate::core::models::user::types::User;
5use crate::core::types::context::RequestContext;
6use crate::utils::error::gateway_error::GatewayError;
7use actix_web::{HttpMessage, HttpRequest, HttpResponse, Result as ActixResult};
8use serde::Serialize;
9use std::future::Future;
10use tracing::{debug, error};
11
12pub fn get_request_context(req: &HttpRequest) -> ActixResult<RequestContext> {
14 if let Some(context) = req.extensions().get::<RequestContext>() {
15 return Ok(context.clone());
16 }
17
18 let mut context = RequestContext::new();
19
20 if let Some(request_id) = req.headers().get("x-request-id")
22 && let Ok(id) = request_id.to_str()
23 {
24 context.request_id = id.to_string();
25 }
26
27 if let Some(user_agent) = req.headers().get("user-agent")
29 && let Ok(agent) = user_agent.to_str()
30 {
31 context.user_agent = Some(agent.to_string());
32 }
33
34 context.client_ip = req.connection_info().peer_addr().map(|ip| ip.to_string());
35
36 Ok(context)
37}
38
39pub fn get_authenticated_user(req: &HttpRequest) -> Option<User> {
41 req.extensions().get::<User>().cloned()
42}
43
44pub fn get_authenticated_api_key(req: &HttpRequest) -> Option<ApiKey> {
46 req.extensions().get::<ApiKey>().cloned()
47}
48
49pub fn check_permission(user: Option<&User>, api_key: Option<&ApiKey>, operation: &str) -> bool {
62 use crate::core::models::user::types::UserRole;
63
64 if user.is_none() && api_key.is_none() {
66 return false;
67 }
68
69 let user_is_admin = user
71 .map(|u| matches!(u.role, UserRole::SuperAdmin | UserRole::Admin))
72 .unwrap_or(false);
73
74 let key_is_admin = api_key
76 .map(|k| {
77 k.permissions
78 .iter()
79 .any(|p| p == "*" || p == "system.admin")
80 })
81 .unwrap_or(false);
82
83 if user_is_admin || key_is_admin {
84 return true;
85 }
86
87 let key_has_operation = api_key
89 .map(|k| k.permissions.iter().any(|p| p == operation))
90 .unwrap_or(false);
91
92 if key_has_operation {
93 return true;
94 }
95
96 let is_management_op = matches!(
98 operation,
99 "keys.list_all" | "users.manage" | "config.manage" | "teams.manage" | "analytics.admin"
100 );
101
102 if is_management_op {
103 return false;
104 }
105
106 true
108}
109
110pub async fn log_api_usage(context: &RequestContext, model: &str, tokens_used: u32, cost: f64) {
112 debug!(
114 "API usage: user_id={:?}, model={}, tokens={}, cost={}",
115 context.user_id, model, tokens_used, cost
116 );
117}
118
119pub async fn handle_ai_request<Req, Resp, F, Fut>(
130 req: &HttpRequest,
131 request: Req,
132 error_label: &str,
133 handler: F,
134) -> ActixResult<HttpResponse>
135where
136 Resp: Serialize,
137 F: FnOnce(Req, RequestContext) -> Fut,
138 Fut: Future<Output = Result<Resp, GatewayError>>,
139{
140 let context = get_request_context(req)?;
141 match handler(request, context).await {
142 Ok(response) => Ok(HttpResponse::Ok().json(response)),
143 Err(e) => {
144 error!("{} error: {}", error_label, e);
145 Ok(super::openai_errors::gateway_error_response(&e))
146 }
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::core::models::user::types::{User, UserRole};
154 use crate::core::models::{Metadata, UsageStats};
155
156 fn create_test_user() -> User {
157 User::new(
158 "testuser".to_string(),
159 "test@example.com".to_string(),
160 "hash".to_string(),
161 )
162 }
163
164 fn create_admin_user() -> User {
165 let mut user = create_test_user();
166 user.role = UserRole::Admin;
167 user
168 }
169
170 fn create_super_admin_user() -> User {
171 let mut user = create_test_user();
172 user.role = UserRole::SuperAdmin;
173 user
174 }
175
176 fn create_test_api_key() -> ApiKey {
177 ApiKey {
178 metadata: Metadata::new(),
179 name: "test-key".to_string(),
180 key_hash: "hash".to_string(),
181 key_prefix: "sk-test".to_string(),
182 user_id: None,
183 team_id: None,
184 permissions: vec![],
185 rate_limits: None,
186 expires_at: None,
187 is_active: true,
188 last_used_at: None,
189 usage_stats: UsageStats::default(),
190 }
191 }
192
193 fn create_admin_api_key() -> ApiKey {
194 let mut key = create_test_api_key();
195 key.permissions = vec!["*".to_string()];
196 key
197 }
198
199 #[test]
202 fn test_check_permission_no_auth() {
203 assert!(!check_permission(None, None, "chat"));
204 }
205
206 #[test]
207 fn test_check_permission_no_auth_management() {
208 assert!(!check_permission(None, None, "keys.list_all"));
209 }
210
211 #[test]
212 fn test_check_permission_with_user() {
213 let user = create_test_user();
214 assert!(check_permission(Some(&user), None, "chat"));
215 }
216
217 #[test]
218 fn test_check_permission_with_api_key() {
219 let api_key = create_test_api_key();
220 assert!(check_permission(None, Some(&api_key), "chat"));
221 }
222
223 #[test]
224 fn test_check_permission_with_both() {
225 let user = create_test_user();
226 let api_key = create_test_api_key();
227 assert!(check_permission(Some(&user), Some(&api_key), "chat"));
228 }
229
230 #[test]
231 fn test_check_permission_various_operations() {
232 let user = create_test_user();
233 assert!(check_permission(Some(&user), None, "chat"));
234 assert!(check_permission(Some(&user), None, "completions"));
235 assert!(check_permission(Some(&user), None, "embeddings"));
236 assert!(check_permission(Some(&user), None, "images"));
237 }
238
239 #[test]
242 fn test_admin_user_can_access_management_ops() {
243 let admin = create_admin_user();
244 assert!(check_permission(Some(&admin), None, "keys.list_all"));
245 assert!(check_permission(Some(&admin), None, "users.manage"));
246 assert!(check_permission(Some(&admin), None, "config.manage"));
247 assert!(check_permission(Some(&admin), None, "teams.manage"));
248 assert!(check_permission(Some(&admin), None, "analytics.admin"));
249 }
250
251 #[test]
252 fn test_super_admin_can_access_management_ops() {
253 let sa = create_super_admin_user();
254 assert!(check_permission(Some(&sa), None, "keys.list_all"));
255 assert!(check_permission(Some(&sa), None, "users.manage"));
256 assert!(check_permission(Some(&sa), None, "config.manage"));
257 }
258
259 #[test]
260 fn test_admin_user_can_access_api_ops() {
261 let admin = create_admin_user();
262 assert!(check_permission(Some(&admin), None, "chat"));
263 assert!(check_permission(Some(&admin), None, "completions"));
264 assert!(check_permission(Some(&admin), None, "models"));
265 }
266
267 #[test]
270 fn test_regular_user_denied_management_ops() {
271 let user = create_test_user();
272 assert!(!check_permission(Some(&user), None, "keys.list_all"));
273 assert!(!check_permission(Some(&user), None, "users.manage"));
274 assert!(!check_permission(Some(&user), None, "config.manage"));
275 assert!(!check_permission(Some(&user), None, "teams.manage"));
276 assert!(!check_permission(Some(&user), None, "analytics.admin"));
277 }
278
279 #[test]
280 fn test_viewer_denied_management_ops() {
281 let mut user = create_test_user();
282 user.role = UserRole::Viewer;
283 assert!(!check_permission(Some(&user), None, "keys.list_all"));
284 assert!(!check_permission(Some(&user), None, "users.manage"));
285 }
286
287 #[test]
288 fn test_api_user_denied_management_ops() {
289 let mut user = create_test_user();
290 user.role = UserRole::ApiUser;
291 assert!(!check_permission(Some(&user), None, "keys.list_all"));
292 assert!(!check_permission(Some(&user), None, "config.manage"));
293 }
294
295 #[test]
296 fn test_manager_denied_management_ops() {
297 let mut user = create_test_user();
298 user.role = UserRole::Manager;
299 assert!(!check_permission(Some(&user), None, "users.manage"));
300 }
301
302 #[test]
305 fn test_admin_api_key_can_access_management_ops() {
306 let key = create_admin_api_key();
307 assert!(check_permission(None, Some(&key), "keys.list_all"));
308 assert!(check_permission(None, Some(&key), "users.manage"));
309 assert!(check_permission(None, Some(&key), "config.manage"));
310 }
311
312 #[test]
313 fn test_system_admin_api_key_can_access_management_ops() {
314 let mut key = create_test_api_key();
315 key.permissions = vec!["system.admin".to_string()];
316 assert!(check_permission(None, Some(&key), "keys.list_all"));
317 assert!(check_permission(None, Some(&key), "users.manage"));
318 }
319
320 #[test]
321 fn test_regular_api_key_denied_management_ops() {
322 let key = create_test_api_key();
323 assert!(!check_permission(None, Some(&key), "keys.list_all"));
324 assert!(!check_permission(None, Some(&key), "users.manage"));
325 }
326
327 #[test]
328 fn test_api_key_with_specific_management_permission() {
329 let mut key = create_test_api_key();
330 key.permissions = vec!["keys.list_all".to_string()];
331 assert!(check_permission(None, Some(&key), "keys.list_all"));
332 assert!(!check_permission(None, Some(&key), "users.manage"));
333 }
334
335 #[test]
338 fn test_get_authenticated_user_returns_none() {
339 let req = actix_web::test::TestRequest::default().to_http_request();
340 assert!(get_authenticated_user(&req).is_none());
341 }
342
343 #[test]
346 fn test_get_authenticated_api_key_returns_none() {
347 let req = actix_web::test::TestRequest::default().to_http_request();
348 assert!(get_authenticated_api_key(&req).is_none());
349 }
350
351 #[tokio::test]
354 async fn test_log_api_usage() {
355 let context = RequestContext::new();
356 log_api_usage(&context, "gpt-4", 100, 0.002).await;
357 }
359
360 #[tokio::test]
361 async fn test_log_api_usage_various_models() {
362 let context = RequestContext::new();
363 log_api_usage(&context, "gpt-3.5-turbo", 50, 0.001).await;
364 log_api_usage(&context, "claude-3-opus", 200, 0.005).await;
365 log_api_usage(&context, "gemini-pro", 75, 0.0015).await;
366 }
367
368 #[tokio::test]
369 async fn test_log_api_usage_zero_tokens() {
370 let context = RequestContext::new();
371 log_api_usage(&context, "gpt-4", 0, 0.0).await;
372 }
373
374 #[tokio::test]
375 async fn test_log_api_usage_large_values() {
376 let context = RequestContext::new();
377 log_api_usage(&context, "gpt-4", 100000, 100.0).await;
378 }
379
380 #[tokio::test]
381 async fn test_log_api_usage_with_user_id() {
382 let mut context = RequestContext::new();
383 context.user_id = Some(uuid::Uuid::new_v4().to_string());
384 log_api_usage(&context, "gpt-4", 100, 0.002).await;
385 }
386
387 #[test]
390 fn test_request_context_new() {
391 let context = RequestContext::new();
392 assert!(context.user_id.is_none());
393 }
394}