use crate::{MiddlewareMechanism, OrdinaryApiLimits, OrdinaryConfig};
use anyhow::bail;
use hashbrown::HashSet;
use regex::Regex;
#[allow(clippy::too_many_lines)]
pub fn check_config_against_limits(
config: &OrdinaryConfig,
limits: &OrdinaryApiLimits,
privileged_domains: &HashSet<String>,
) -> anyhow::Result<()> {
if privileged_domains.contains(&config.domain) {
return Ok(());
}
if let Some(canonical) = &config.canonical {
let mut is_valid = false;
if canonical == &config.domain {
is_valid = true;
}
if let Some(cnames) = &config.cnames {
for cname in cnames {
if cname == canonical {
is_valid = true;
break;
}
}
}
if !is_valid {
bail!("canonical: {canonical}, is not in 'domain' or 'cnames'");
}
}
if let Some(default_timeout) = config.default_timeout
&& default_timeout > limits.max_default_timeout
{
bail!(
"default timeout {} greater than limit {}",
default_timeout,
limits.max_default_timeout
);
}
if let Some(proxies) = &config.proxies {
if proxies.len() > limits.proxy.count as usize {
bail!(
"proxy count {} greater than limit {}",
proxies.len(),
limits.proxy.count
);
}
let mut disallowed_target_regexes =
Vec::with_capacity(limits.proxy.disallowed_targets.len());
for disallowed in &limits.proxy.disallowed_targets {
disallowed_target_regexes.push(Regex::new(disallowed)?);
}
for proxy in proxies {
for re in &disallowed_target_regexes {
if re.is_match(&proxy.target) {
bail!(
"proxy target {} is disallowed by rule {}",
proxy.target,
re.as_str()
)
}
}
}
}
if let Some(middlewares) = &config.middlewares {
if middlewares.len() > limits.middleware.count as usize {
bail!(
"middlewares count {} greater than limit {}",
middlewares.len(),
limits.middleware.count
);
}
let mut disallowed_endpoint_regexes =
Vec::with_capacity(limits.middleware.disallowed_endpoints.len());
for disallowed in &limits.middleware.disallowed_endpoints {
disallowed_endpoint_regexes.push(Regex::new(disallowed)?);
}
for middleware in middlewares {
match &middleware.mechanism {
MiddlewareMechanism::Request { endpoint } => {
for re in &disallowed_endpoint_regexes {
if re.is_match(endpoint) {
bail!(
"middleware endpoint {endpoint} is disallowed by rule {}",
re.as_str()
)
}
}
}
}
}
}
if let Some(templates) = &config.templates {
if templates.len() > limits.template.count as usize {
bail!(
"template count {} greater than limit {}",
templates.len(),
limits.template.count
);
}
for template in templates {
if let Some(timeout) = template.timeout
&& timeout > limits.template.max_timeout
{
bail!(
"template timeout {} greater than limit {} for {}",
timeout,
limits.template.max_timeout,
template.name
);
}
if let Some(cache) = &template.cache
&& let Some(stored_cache) = &cache.stored
{
if let Some(max_size) = stored_cache.max_size
&& (max_size < limits.storage.cache.max_size_range.0
|| max_size > limits.storage.cache.max_size_range.1)
{
bail!(
"template cache max_size {} is not within range [{}, {}] for {}",
max_size,
limits.storage.cache.max_size_range.0,
limits.storage.cache.max_size_range.1,
template.name
);
}
if let Some(max_count) = stored_cache.max_count
&& (max_count < limits.storage.cache.max_count_range.0
|| max_count > limits.storage.cache.max_count_range.1)
{
bail!(
"template cache max_count {} is not within range [{}, {}] for {}",
max_count,
limits.storage.cache.max_count_range.0,
limits.storage.cache.max_count_range.1,
template.name
);
}
if let Some((clean_interval_min, clean_interval_max)) = stored_cache.clean_interval
{
if clean_interval_min < limits.storage.cache.clean_interval_ranges.0.0
|| clean_interval_min > limits.storage.cache.clean_interval_ranges.0.1
{
bail!(
"template cache clean_interval min {} is not within range [{}, {}] for {}",
clean_interval_min,
limits.storage.cache.clean_interval_ranges.0.0,
limits.storage.cache.clean_interval_ranges.0.1,
template.name
);
}
if clean_interval_max < limits.storage.cache.clean_interval_ranges.1.0
|| clean_interval_max > limits.storage.cache.clean_interval_ranges.1.1
{
bail!(
"template cache clean_interval min {} is not within range [{}, {}] for {}",
clean_interval_max,
limits.storage.cache.clean_interval_ranges.1.0,
limits.storage.cache.clean_interval_ranges.1.1,
template.name
);
}
}
}
}
}
if let Some(integrations) = &config.integrations {
if integrations.len() > limits.integration.count as usize {
bail!(
"integration count {} greater than limit {}",
integrations.len(),
limits.integration.count
);
}
for integration in integrations {
if let Some(timeout) = integration.timeout
&& timeout > limits.integration.max_timeout
{
bail!(
"integration timeout {} greater than limit {} for {}",
timeout,
limits.integration.max_timeout,
integration.name,
);
}
}
}
if let Some(actions) = &config.actions {
if actions.len() > limits.action.count as usize {
bail!(
"action count {} greater than limit {}",
actions.len(),
limits.action.count
);
}
for action in actions {
if action.privileged == Some(true) {
bail!("action {} is not under a privileged domain", action.name);
}
if let Some(timeout) = action.timeout
&& timeout > limits.action.max_timeout
{
bail!(
"action timeout {} greater than limit {} for {}",
timeout,
limits.action.max_timeout,
action.name
);
}
}
}
if let Some(storage_size) = config.storage_size
&& storage_size > limits.storage.max_app_storage
{
bail!("storage size is greater than limit");
}
if let Some(content) = &config.content {
if content.definitions.len() > limits.storage.content.max_content_definitions as usize {
bail!(
"content definition count {} greater than limit {}",
content.definitions.len(),
limits.storage.content.max_content_definitions
);
}
for content_def in &content.definitions {
if content_def.fields.len() > limits.storage.content.max_content_fields as usize {
bail!(
"content field count {} greater than limit {} for {}",
content_def.fields.len(),
limits.storage.content.max_content_fields,
content_def.name,
);
}
for field in &content_def.fields {
if field.searchable == Some(true) && !limits.storage.content.search_enabled {
bail!(
"content field cannot be 'searchable' for field {} on definition {}",
field.name,
content_def.name,
);
}
}
}
}
if let Some(models) = &config.models {
if models.len() > limits.storage.model.max_model_definitions as usize {
bail!(
"model definition count {} greater than limit {}",
models.len(),
limits.storage.model.max_model_definitions
);
}
for model in models {
if model.fields.len() > limits.storage.model.max_model_fields as usize {
bail!(
"model field count {} greater than limit {} for {}",
model.fields.len(),
limits.storage.content.max_content_fields,
model.name,
);
}
for field in &model.fields {
if field.searchable == Some(true) && !limits.storage.content.search_enabled {
bail!(
"content field cannot be 'searchable' for field {} on definition {}",
field.name,
model.name,
);
}
}
}
}
if let Some(secrets) = &config.secrets
&& secrets.len() > limits.storage.secrets.max_count as usize
{
bail!(
"secrets len {} exceeds max count limit {}",
secrets.len(),
limits.storage.secrets.max_count
);
}
Ok(())
}