1use std::path::Path;
7use std::sync::Arc;
8
9use tonic::metadata::MetadataMap;
10use tonic::service::Interceptor;
11use tonic::{Request, Status};
12
13pub trait Authenticator: Send + Sync + 'static {
15 fn authenticate(&self, bearer: Option<&str>) -> bool;
17}
18
19pub struct StaticToken(Option<String>);
21
22impl StaticToken {
23 #[must_use]
25 pub const fn new(token: Option<String>) -> Self {
26 Self(token)
27 }
28}
29
30impl Authenticator for StaticToken {
31 fn authenticate(&self, bearer: Option<&str>) -> bool {
32 match &self.0 {
33 None => true,
34 Some(expected) => bearer == Some(expected.as_str()),
35 }
36 }
37}
38
39fn bearer(metadata: &MetadataMap) -> Option<&str> {
40 metadata
41 .get("authorization")
42 .and_then(|value| value.to_str().ok())
43 .and_then(|value| value.strip_prefix("Bearer "))
44}
45
46#[derive(Clone)]
48pub struct AuthInterceptor(Arc<dyn Authenticator>);
49
50impl AuthInterceptor {
51 #[must_use]
53 pub fn new(authenticator: Arc<dyn Authenticator>) -> Self {
54 Self(authenticator)
55 }
56}
57
58impl Interceptor for AuthInterceptor {
59 fn call(&mut self, request: Request<()>) -> Result<Request<()>, Status> {
60 if self.0.authenticate(bearer(request.metadata())) {
61 Ok(request)
62 } else {
63 Err(Status::unauthenticated("invalid or missing bearer token"))
64 }
65 }
66}
67
68#[derive(Clone, Default)]
70pub struct TokenInterceptor(Option<String>);
71
72impl TokenInterceptor {
73 #[must_use]
75 pub const fn new(token: Option<String>) -> Self {
76 Self(token)
77 }
78}
79
80impl Interceptor for TokenInterceptor {
81 fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
82 if let Some(token) = &self.0 {
83 let value = format!("Bearer {token}")
84 .parse()
85 .map_err(|_| Status::internal("token is not valid metadata"))?;
86 request.metadata_mut().insert("authorization", value);
87 }
88 Ok(request)
89 }
90}
91
92pub fn server_tls(
97 cert_pem: &Path,
98 key_pem: &Path,
99) -> Result<tonic::transport::ServerTlsConfig, std::io::Error> {
100 let cert = std::fs::read(cert_pem)?;
101 let key = std::fs::read(key_pem)?;
102 Ok(tonic::transport::ServerTlsConfig::new()
103 .identity(tonic::transport::Identity::from_pem(cert, key)))
104}
105
106pub fn client_tls(
112 ca_pem: Option<&Path>,
113 domain: Option<&str>,
114) -> Result<tonic::transport::ClientTlsConfig, std::io::Error> {
115 let mut config = tonic::transport::ClientTlsConfig::new().with_enabled_roots();
116 if let Some(path) = ca_pem {
117 let ca = std::fs::read(path)?;
118 config = config.ca_certificate(tonic::transport::Certificate::from_pem(ca));
119 }
120 if let Some(domain) = domain {
121 config = config.domain_name(domain);
122 }
123 Ok(config)
124}