use std::collections::HashMap;
use tracing::warn;
use typesec_core::glob::GlobPattern;
use crate::model::{OdrlDocument, RuleAction};
pub(super) type RuleKey = (String, String);
pub(super) type RuleIndex = HashMap<RuleKey, Vec<RuleRef>>;
pub(super) type WildcardActionIndex = HashMap<String, Vec<RuleRef>>;
#[derive(Debug, Clone)]
pub(super) struct CompiledTarget {
raw: String,
pattern: Option<GlobPattern>,
}
impl CompiledTarget {
fn compile(target: &str) -> Self {
let stripped = target.strip_prefix("asset:").unwrap_or(target);
let pattern = match GlobPattern::compile(stripped, "target") {
Ok(pattern) => Some(pattern),
Err(err) => {
warn!(target, %err, "ODRL target is not a valid glob; it will never match");
None
}
};
Self {
raw: target.to_owned(),
pattern,
}
}
pub(super) fn matches(&self, resource: &str) -> bool {
self.raw == resource || self.pattern.as_ref().is_some_and(|p| p.matches(resource))
}
}
pub(super) fn compile_targets(doc: &OdrlDocument) -> HashMap<(usize, usize), CompiledTarget> {
let mut targets = HashMap::new();
for (policy_index, policy) in doc.policies.iter().enumerate() {
for (rule_index, rule) in policy.rules.iter().enumerate() {
targets.insert(
(policy_index, rule_index),
CompiledTarget::compile(&rule.target),
);
}
}
targets
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct RuleRef {
pub(super) policy_index: usize,
pub(super) rule_index: usize,
pub(super) ordinal: usize,
}
pub(super) fn build_rule_index(doc: &OdrlDocument) -> (RuleIndex, WildcardActionIndex) {
let mut exact_rules: RuleIndex = HashMap::new();
let mut wildcard_action_rules: WildcardActionIndex = HashMap::new();
let mut ordinal = 0;
for (policy_index, policy) in doc.policies.iter().enumerate() {
for (rule_index, rule) in policy.rules.iter().enumerate() {
let rule_ref = RuleRef {
policy_index,
rule_index,
ordinal,
};
ordinal += 1;
if matches!(rule.action, RuleAction::Use) {
wildcard_action_rules
.entry(rule.assignee.clone())
.or_default()
.push(rule_ref);
} else {
exact_rules
.entry((
rule.assignee.clone(),
rule.action.as_permission_name().to_owned(),
))
.or_default()
.push(rule_ref);
}
}
}
(exact_rules, wildcard_action_rules)
}