use crate::config::ConfigSet;
use crate::rev_list::ObjectFilter;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum UploadFilterError {
#[error("unable to parse uploadpackfilter.tree.maxdepth")]
InvalidTreeMaxDepth,
#[error("filter '{0}' not supported")]
UnsupportedFilter(String),
#[error("tree filter allows max depth {max}, but got {actual}")]
TreeDepthTooLarge {
max: u64,
actual: u64,
},
#[error("{0}")]
InvalidFilterSpec(String),
}
pub fn validate_upload_filter_config(config: &ConfigSet) -> Result<(), UploadFilterError> {
tree_max_depth(config).map(|_| ())
}
pub fn validate_upload_filter_request(
config: &ConfigSet,
spec: &str,
) -> Result<(), UploadFilterError> {
let filter = ObjectFilter::parse(spec).map_err(UploadFilterError::InvalidFilterSpec)?;
validate_filter(config, &filter)
}
fn tree_max_depth(config: &ConfigSet) -> Result<Option<u64>, UploadFilterError> {
config
.get("uploadpackfilter.tree.maxdepth")
.map(|value| {
value
.trim()
.parse::<u64>()
.map_err(|_| UploadFilterError::InvalidTreeMaxDepth)
})
.transpose()
}
fn validate_filter(config: &ConfigSet, filter: &ObjectFilter) -> Result<(), UploadFilterError> {
match filter {
ObjectFilter::BlobNone => ensure_filter_allowed(config, "blob:none", "blob:none"),
ObjectFilter::BlobLimit(_) => ensure_filter_allowed(config, "blob:limit", "blob:limit"),
ObjectFilter::TreeDepth(depth) => {
ensure_filter_allowed(config, "tree", "tree")?;
if let Some(max) = tree_max_depth(config)? {
if *depth > max {
return Err(UploadFilterError::TreeDepthTooLarge {
max,
actual: *depth,
});
}
}
Ok(())
}
ObjectFilter::SparseOid(_) => ensure_filter_allowed(config, "sparse:oid", "sparse:oid"),
ObjectFilter::ObjectType(_) => ensure_filter_allowed(config, "object:type", "object:type"),
ObjectFilter::Combine(filters) => {
ensure_filter_allowed(config, "combine", "combine")?;
for filter in filters {
validate_filter(config, filter)?;
}
Ok(())
}
}
}
fn ensure_filter_allowed(
config: &ConfigSet,
config_kind: &str,
display_kind: &str,
) -> Result<(), UploadFilterError> {
let key = format!("uploadpackfilter.{config_kind}.allow");
if let Some(value) = config.get_bool(&key) {
if value.unwrap_or(false) {
return Ok(());
}
return Err(UploadFilterError::UnsupportedFilter(
display_kind.to_owned(),
));
}
if config
.get_bool("uploadpackfilter.allow")
.map(|value| value.unwrap_or(false))
.unwrap_or(true)
{
Ok(())
} else {
Err(UploadFilterError::UnsupportedFilter(
display_kind.to_owned(),
))
}
}