///|
/// A deny rule takes precedence over permits. Without a matching rule,
/// evaluation uses default deny.
pub fn evaluate_request(
policy : AccessPolicy,
universe : RequestUniverse,
request : AccessRequest,
) -> Decision {
let mut principal : Principal? = None
let mut resource : AccessResource? = None
for candidate in universe.principals {
if candidate.id == request.principal_id {
principal = Some(candidate)
break
}
}
for candidate in universe.resources {
if candidate.id == request.resource_id {
resource = Some(candidate)
break
}
}
match (principal, resource) {
(None, _) => return unknown_decision("Principal is not in the universe")
(_, None) => return unknown_decision("Resource is not in the universe")
(Some(principal), Some(resource)) => {
let roles = effective_roles(policy, principal)
let traces : Array[RuleTrace] = []
let permits : Array[String] = []
let denies : Array[String] = []
for rule in policy.rules {
let trace = rule_match_trace(rule, roles, principal, resource, request)
traces.push(trace)
if trace.matched {
match trace.effect {
Permit => permits.push(trace.rule_id)
Deny => denies.push(trace.rule_id)
}
}
}
if denies.length() > 0 {
return {
kind: Denied,
decisive_rules: denies,
traces,
explanation: "A deny rule matched",
}
}
if permits.length() > 0 {
return {
kind: Allowed,
decisive_rules: permits,
traces,
explanation: "A permit rule matched",
}
}
{ kind: Denied, decisive_rules: [], traces, explanation: "Default deny", }
}
}
}
///|
pub fn evaluate_validated(
policy : AccessPolicy,
universe : RequestUniverse,
request : AccessRequest,
) -> Decision {
if has_errors(validate_policy(policy)) ||
has_errors(validate_universe(universe)) {
return unknown_decision("Invalid policy or request universe")
}
evaluate_request(policy, universe, request)
}