Rust implementation of OCI Distribution Spec with granular access control
at main 82 lines 2.5 kB view raw
1use axum::{extract::Request, middleware::Next, response::Response}; 2use std::time::Instant; 3 4use crate::metrics; 5 6pub async fn track_metrics(req: Request, next: Next) -> Response { 7 let start = Instant::now(); 8 let method = req.method().to_string(); 9 let path = req.uri().path().to_string(); 10 11 // Process request 12 let response = next.run(req).await; 13 14 // Record metrics 15 let duration = start.elapsed().as_secs_f64(); 16 let status = response.status().as_u16().to_string(); 17 18 // Normalize endpoint for metrics (avoid cardinality explosion) 19 let endpoint = normalize_endpoint(&path); 20 21 metrics::HTTP_REQUESTS_TOTAL 22 .with_label_values(&[&method, &endpoint, &status]) 23 .inc(); 24 25 metrics::REQUEST_DURATION 26 .with_label_values(&[&method, &endpoint]) 27 .observe(duration); 28 29 response 30} 31 32fn normalize_endpoint(path: &str) -> String { 33 // Replace dynamic segments with placeholders 34 if path == "/v2/" { 35 return "/v2/".to_string(); 36 } 37 if path.starts_with("/v2/") { 38 if path.contains("/blobs/") { 39 if path.contains("/uploads/") { 40 return "/v2/{name}/blobs/uploads/{reference}".to_string(); 41 } 42 return "/v2/{name}/blobs/{digest}".to_string(); 43 } else if path.contains("/manifests/") { 44 return "/v2/{name}/manifests/{reference}".to_string(); 45 } else if path.contains("/tags/") { 46 return "/v2/{name}/tags/list".to_string(); 47 } 48 } 49 if path.starts_with("/admin/") { 50 if path.contains("/users/") && path.split('/').count() > 3 { 51 if path.contains("/permissions") { 52 return "/admin/users/{username}/permissions".to_string(); 53 } 54 return "/admin/users/{username}".to_string(); 55 } 56 return path.to_string(); 57 } 58 path.to_string() 59} 60 61#[cfg(test)] 62mod tests { 63 use super::*; 64 65 #[test] 66 fn test_normalize_endpoint() { 67 assert_eq!( 68 normalize_endpoint("/v2/myorg/myrepo/blobs/sha256:abc123"), 69 "/v2/{name}/blobs/{digest}" 70 ); 71 assert_eq!( 72 normalize_endpoint("/v2/myorg/myrepo/manifests/latest"), 73 "/v2/{name}/manifests/{reference}" 74 ); 75 assert_eq!( 76 normalize_endpoint("/v2/myorg/myrepo/tags/list"), 77 "/v2/{name}/tags/list" 78 ); 79 assert_eq!(normalize_endpoint("/health"), "/health"); 80 assert_eq!(normalize_endpoint("/metrics"), "/metrics"); 81 } 82}