use std::sync::Arc;
use tonic::{Request, Status};
pub(crate) fn auth_interceptor(
req: Request<()>,
api_keys: &[brain::ApiKeyConfig],
rate_limits: Option<&Arc<::resilience::RateLimitRegistry>>,
) -> Result<Request<()>, Status> {
let metadata = req.metadata();
let provided_key = metadata
.get("x-api-key")
.and_then(|v| v.to_str().ok())
.or_else(|| {
metadata.get("authorization").and_then(|v| {
v.to_str()
.ok()
.and_then(|s| brain::auth::extract_bearer_from_value(s).or(Some(s)))
})
});
let result = brain::check_auth(api_keys, provided_key, "write");
match result {
brain::AuthResult::Allowed => {}
brain::AuthResult::InsufficientPermission => {
return Err(Status::permission_denied(
result.error_message("write").unwrap_or_default(),
));
}
_ => {
return Err(Status::unauthenticated(
result.error_message("write").unwrap_or_default(),
));
}
}
if let (Some(registry), Some(key)) = (rate_limits, provided_key) {
let limiter = registry.get_or_create(key);
if !limiter.try_acquire() {
tracing::warn!("gRPC rate limit exceeded for client");
return Err(Status::resource_exhausted("Too many requests"));
}
}
Ok(req)
}
pub(crate) async fn resolve_principal_from_metadata<T>(
req: &Request<T>,
api_keys: &[brain::ApiKeyConfig],
processor: &signal::SignalProcessor,
) -> Option<identity::Principal> {
let metadata = req.metadata();
let key = metadata
.get("x-api-key")
.and_then(|v| v.to_str().ok())
.or_else(|| {
metadata.get("authorization").and_then(|v| {
v.to_str()
.ok()
.and_then(|s| brain::auth::extract_bearer_from_value(s).or(Some(s)))
})
})?;
let agent_id = brain::auth::find_key_ct(api_keys, key).and_then(|k| k.agent_id.clone())?;
let store = processor.identity_store()?;
store
.principal_for(&identity::AgentHint::AgentId(agent_id.into()))
.await
.ok()
}