use crate::algebra::{JoinSemilattice, MeetSemilattice};
use crate::permission::GrantDenialPair;
#[cfg(test)]
use crate::permission::PermissionSet;
pub trait ConflictResolver {
fn resolve(&self, results: Vec<GrantDenialPair>) -> GrantDenialPair;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct MeetResolver;
impl ConflictResolver for MeetResolver {
fn resolve(&self, results: Vec<GrantDenialPair>) -> GrantDenialPair {
if results.is_empty() {
return GrantDenialPair::empty();
}
results
.into_iter()
.reduce(|a, b| a.meet(b))
.unwrap_or_else(GrantDenialPair::empty)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct JoinResolver;
impl ConflictResolver for JoinResolver {
fn resolve(&self, results: Vec<GrantDenialPair>) -> GrantDenialPair {
if results.is_empty() {
return GrantDenialPair::empty();
}
results
.into_iter()
.reduce(|a, b| a.join(b))
.unwrap_or_else(GrantDenialPair::empty)
}
}
#[derive(Debug, Clone, Default)]
pub struct PriorityResolver {
priorities: std::collections::HashMap<usize, u32>,
}
impl PriorityResolver {
pub fn new() -> Self {
Self::default()
}
pub fn set_priority(&mut self, rule_id: usize, priority: u32) {
self.priorities.insert(rule_id, priority);
}
pub fn resolve_with_ids(&self, results: Vec<(usize, GrantDenialPair)>) -> GrantDenialPair {
if results.is_empty() {
return GrantDenialPair::empty();
}
let max_priority = results
.iter()
.map(|(id, _)| self.priorities.get(id).copied().unwrap_or(0))
.max()
.unwrap_or(0);
let highest_priority_results: Vec<_> = results
.into_iter()
.filter(|(id, _)| self.priorities.get(id).copied().unwrap_or(0) == max_priority)
.map(|(_, result)| result)
.collect();
MeetResolver.resolve(highest_priority_results)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::permission::AtomicPermission;
#[test]
fn test_meet_resolver() {
let resolver = MeetResolver;
let r1 = GrantDenialPair::new(
PermissionSet::from([
AtomicPermission::new("file", "read"),
AtomicPermission::new("file", "write"),
]),
PermissionSet::new(),
);
let r2 = GrantDenialPair::new(
PermissionSet::from([AtomicPermission::new("file", "read")]),
PermissionSet::from([AtomicPermission::new("file", "delete")]),
);
let result = resolver.resolve(vec![r1, r2]);
let effective = result.effective_permissions();
assert_eq!(effective.len(), 1);
assert!(effective.contains(&AtomicPermission::new("file", "read")));
}
#[test]
fn test_join_resolver() {
let resolver = JoinResolver;
let r1 = GrantDenialPair::new(
PermissionSet::from([AtomicPermission::new("file", "read")]),
PermissionSet::new(),
);
let r2 = GrantDenialPair::new(
PermissionSet::from([AtomicPermission::new("file", "write")]),
PermissionSet::new(),
);
let result = resolver.resolve(vec![r1, r2]);
let effective = result.effective_permissions();
assert_eq!(effective.len(), 2);
}
#[test]
fn test_priority_resolver() {
let mut resolver = PriorityResolver::new();
resolver.set_priority(0, 10);
resolver.set_priority(1, 5);
let r1 = GrantDenialPair::new(
PermissionSet::from([AtomicPermission::new("file", "read")]),
PermissionSet::new(),
);
let r2 = GrantDenialPair::new(
PermissionSet::from([AtomicPermission::new("file", "write")]),
PermissionSet::new(),
);
let result = resolver.resolve_with_ids(vec![(0, r1), (1, r2)]);
let effective = result.effective_permissions();
assert_eq!(effective.len(), 1);
assert!(effective.contains(&AtomicPermission::new("file", "read")));
}
}