use systemprompt_identifiers::UserId;
use super::types::{Access, AccessRule, Decision, DenyReason, EntityRef, MatchedBy, RuleType};
#[derive(Debug, Clone, Copy)]
pub struct ResolveInput<'a> {
pub entity: &'a EntityRef,
pub rules: &'a [AccessRule],
pub user_id: &'a UserId,
pub user_roles: &'a [String],
pub department: &'a str,
pub default_included: Option<bool>,
}
#[must_use]
pub fn resolve(input: ResolveInput<'_>) -> Decision {
let ResolveInput {
entity,
rules,
user_id,
user_roles,
department,
default_included,
} = input;
let Some(default_included) = default_included else {
return Decision::Deny {
reason: DenyReason::UnknownEntity {
entity: entity.clone(),
},
};
};
let user_match =
|r: &AccessRule| r.rule_type == RuleType::User && r.rule_value == user_id.as_str();
let role_match = |r: &AccessRule| {
r.rule_type == RuleType::Role && user_roles.iter().any(|role| role == &r.rule_value)
};
let dept_match = |r: &AccessRule| {
r.rule_type == RuleType::Department && r.rule_value == department && !department.is_empty()
};
if let Some(rule) = rules
.iter()
.find(|r| user_match(r) && r.access == Access::Deny)
{
return Decision::Deny {
reason: DenyReason::UserDeny {
entity: entity.clone(),
user_id: user_id.clone(),
justification: rule.justification.clone(),
},
};
}
if rules
.iter()
.any(|r| user_match(r) && r.access == Access::Allow)
{
return Decision::Allow {
matched_by: MatchedBy::UserAllow,
};
}
if let Some(rule) = rules
.iter()
.find(|r| role_match(r) && r.access == Access::Deny)
{
return Decision::Deny {
reason: DenyReason::RoleDeny {
entity: entity.clone(),
role: rule.rule_value.clone(),
justification: rule.justification.clone(),
},
};
}
if let Some(rule) = rules
.iter()
.find(|r| role_match(r) && r.access == Access::Allow)
{
return Decision::Allow {
matched_by: MatchedBy::RoleAllow {
role: rule.rule_value.clone(),
},
};
}
if let Some(rule) = rules
.iter()
.find(|r| dept_match(r) && r.access == Access::Deny)
{
return Decision::Deny {
reason: DenyReason::DepartmentDeny {
entity: entity.clone(),
department: rule.rule_value.clone(),
justification: rule.justification.clone(),
},
};
}
if rules
.iter()
.any(|r| dept_match(r) && r.access == Access::Allow)
{
return Decision::Allow {
matched_by: MatchedBy::DepartmentAllow {
department: department.to_owned(),
},
};
}
if default_included {
return Decision::Allow {
matched_by: MatchedBy::DefaultIncluded,
};
}
Decision::Deny {
reason: DenyReason::NotAssigned {
entity: entity.clone(),
user_id: user_id.clone(),
roles: user_roles.to_vec(),
},
}
}