use serde::{Deserialize, Serialize};
use super::error::AuthzError;
use super::types::{Access, EntityKind};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AccessControlConfig {
#[serde(default)]
pub rules: Vec<RuleEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RuleEntry {
pub entity_type: EntityKind,
pub entity_id: String,
pub access: Access,
#[serde(default)]
pub roles: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub justification: Option<String>,
}
impl AccessControlConfig {
pub fn validate(&self) -> Result<(), AuthzError> {
let mut problems: Vec<String> = Vec::new();
for (idx, rule) in self.rules.iter().enumerate() {
if rule.entity_id.trim().is_empty() {
problems.push(format!("rules[{idx}]: entity_id is empty"));
}
if rule.roles.is_empty() {
problems.push(format!(
"rules[{idx}]: must declare at least one role — per-user rules belong to \
runtime state, not YAML, and attribute-based rules belong in an extension \
hook"
));
}
for role in &rule.roles {
if role.trim().is_empty() {
problems.push(format!("rules[{idx}]: empty role string"));
}
}
}
if problems.is_empty() {
Ok(())
} else {
Err(AuthzError::Validation(problems.join("; ")))
}
}
}