use std::sync::Arc;
use tonic::{Request, Status, service::Interceptor};
use crate::session::{ClientId, SessionToken, TokenRegistry};
#[derive(Clone)]
pub struct AuthInterceptor {
tokens: Arc<TokenRegistry>,
}
impl AuthInterceptor {
#[must_use]
pub const fn new(tokens: Arc<TokenRegistry>) -> Self {
Self { tokens }
}
}
impl Interceptor for AuthInterceptor {
fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
if let Some(token_str) = request
.metadata()
.get("x-reovim-token")
.and_then(|v| v.to_str().ok())
{
let token = SessionToken::from(token_str);
if let Some(client_id) = self.tokens.resolve(&token) {
request.extensions_mut().insert(client_id);
}
}
Ok(request)
}
}
#[allow(clippy::result_large_err)]
pub fn require_client_id(token_client_id: Option<ClientId>) -> Result<ClientId, Status> {
token_client_id
.ok_or_else(|| Status::unauthenticated("session token required — call Join() first"))
}
#[allow(clippy::result_large_err)]
#[allow(clippy::cast_possible_truncation)]
pub fn resolve_target_client_id(
token_client_id: Option<ClientId>,
target_client_id: u64,
) -> Result<ClientId, Status> {
let caller = require_client_id(token_client_id)?;
if target_client_id == 0 {
return Ok(caller);
}
Ok(ClientId::new(target_client_id as usize))
}
#[cfg(test)]
#[path = "auth_tests.rs"]
mod tests;