datasynth_server/grpc/
auth_interceptor.rs1use tonic::{Request, Status};
6
7pub type ApiKeyValidator = Box<dyn Fn(&str) -> bool + Send + Sync>;
9
10#[derive(Clone)]
12pub struct GrpcAuthConfig {
13 pub enabled: bool,
15 api_keys: Vec<String>,
17}
18
19impl GrpcAuthConfig {
20 pub fn new(api_keys: Vec<String>) -> Self {
22 Self {
23 enabled: !api_keys.is_empty(),
24 api_keys,
25 }
26 }
27
28 pub fn disabled() -> Self {
30 Self {
31 enabled: false,
32 api_keys: Vec::new(),
33 }
34 }
35
36 pub fn validate_token(&self, token: &str) -> bool {
38 if !self.enabled {
39 return true;
40 }
41 self.api_keys.iter().any(|k| k == token)
42 }
43}
44
45#[allow(clippy::result_large_err)]
49pub fn auth_interceptor(config: &GrpcAuthConfig, request: &Request<()>) -> Result<(), Status> {
50 if !config.enabled {
51 return Ok(());
52 }
53
54 let token = request
55 .metadata()
56 .get("authorization")
57 .and_then(|v| v.to_str().ok())
58 .and_then(|s| s.strip_prefix("Bearer "));
59
60 match token {
61 Some(t) if config.validate_token(t) => Ok(()),
62 Some(_) => Err(Status::unauthenticated("Invalid credentials")),
63 None => Err(Status::unauthenticated(
64 "Missing authorization metadata. Provide 'authorization: Bearer <token>'",
65 )),
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_disabled_auth_passes() {
75 let config = GrpcAuthConfig::disabled();
76 let request = Request::new(());
77 assert!(auth_interceptor(&config, &request).is_ok());
78 }
79
80 #[test]
81 fn test_missing_token_fails() {
82 let config = GrpcAuthConfig::new(vec!["secret".to_string()]);
83 let request = Request::new(());
84 assert!(auth_interceptor(&config, &request).is_err());
85 }
86
87 #[test]
88 fn test_valid_token_passes() {
89 let config = GrpcAuthConfig::new(vec!["my-key".to_string()]);
90 let mut request = Request::new(());
91 request
92 .metadata_mut()
93 .insert("authorization", "Bearer my-key".parse().unwrap());
94 assert!(auth_interceptor(&config, &request).is_ok());
95 }
96
97 #[test]
98 fn test_invalid_token_fails() {
99 let config = GrpcAuthConfig::new(vec!["my-key".to_string()]);
100 let mut request = Request::new(());
101 request
102 .metadata_mut()
103 .insert("authorization", "Bearer wrong-key".parse().unwrap());
104 assert!(auth_interceptor(&config, &request).is_err());
105 }
106}