use dataflow_rs::datalogic_rs;
use std::sync::Arc;
use serde_json::{Value, json};
use super::ChannelRuntimeConfig;
use super::*;
use crate::errors::OrionError;
use crate::metrics;
pub(super) async fn check_auth(
channel: &str,
channel_config: &Option<Arc<ChannelRuntimeConfig>>,
header: HeaderLookup<'_>,
raw_body: Option<&[u8]>,
datalogic: &datalogic_rs::Engine,
backoff: Option<&crate::auth::FailedAuthTracker>,
client: &str,
) -> Result<Option<Value>, OrionError> {
let Some(cfg) = channel_config else {
return Ok(None);
};
let Some(ref auth) = cfg.auth else {
return Ok(None);
};
let budget = backoff
.filter(|_| auth.failures_are_guesses())
.map(|tracker| (tracker, format!("{channel}\u{1f}{client}")));
if let Some((tracker, key)) = &budget
&& let Some(remaining) = tracker.locked_for(key)
{
crate::metrics::record_error("channel_auth_locked_out");
tracing::warn!(
channel = %channel,
remaining_ms = remaining.as_millis() as u64,
"Channel authentication refused: client is in failed-auth backoff"
);
return Err(crate::channel::auth::refused());
}
let outcome = auth
.authenticate(header, raw_body, datalogic)
.await
.map(|outcome| outcome.claims)
.inspect_err(|_| {
metrics::record_message(channel, "unauthorized");
tracing::warn!(channel = %channel, "Channel authentication failed");
});
if let Some((tracker, key)) = &budget {
match &outcome {
Ok(_) => tracker.record_success(key),
Err(_) => {
if let Some(lockout) = tracker.record_failure(key) {
tracing::warn!(
channel = %channel,
lockout_ms = lockout.as_millis() as u64,
"Channel authentication: client entered failed-auth backoff"
);
}
}
}
}
outcome
}
pub(super) fn check_allowed_origin(
channel: &str,
channel_config: &Option<Arc<ChannelRuntimeConfig>>,
origin: Option<&str>,
) -> Result<(), OrionError> {
if let Some(cfg) = channel_config
&& let Some(allowed_origins) = cfg.parsed_config.allowed_origins()
&& let Some(origin) = origin
&& !allowed_origins.iter().any(|o| o == "*" || o == origin)
{
return Err(OrionError::Forbidden(format!(
"Origin '{origin}' is not allowed for channel '{channel}'"
)));
}
Ok(())
}
pub(super) fn validate_input(
channel: &str,
channel_config: &Option<Arc<ChannelRuntimeConfig>>,
data: &Value,
metadata: &Value,
datalogic: &datalogic_rs::Engine,
) -> Result<(), OrionError> {
if let Some(cfg) = channel_config
&& let Some(ref compiled) = cfg.validation_logic
{
let context = json!({ "data": data, "metadata": metadata });
match datalogic
.session()
.eval_into::<serde_json::Value, _>(compiled, &context)
{
Ok(result) => {
if !is_truthy(&result) {
return Err(OrionError::validation(
"Input validation failed".to_string(),
));
}
}
Err(e) => {
tracing::warn!(channel = %channel, error = %e, "validation_logic evaluation failed, rejecting");
return Err(OrionError::validation(
"Input validation failed".to_string(),
));
}
}
}
Ok(())
}
pub(super) fn acquire_backpressure(
channel: &str,
channel_config: &Option<Arc<ChannelRuntimeConfig>>,
) -> Result<Option<tokio::sync::OwnedSemaphorePermit>, OrionError> {
if let Some(cfg) = channel_config
&& let Some(ref semaphore) = cfg.backpressure_semaphore
{
match semaphore.clone().try_acquire_owned() {
Ok(permit) => Ok(Some(permit)),
Err(_) => {
metrics::record_error("backpressure");
Err(OrionError::unavailable(
crate::errors::Unavailable::AtCapacity,
format!("Channel '{channel}' is at capacity"),
))
}
}
} else {
Ok(None)
}
}