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