use std::fmt;
use std::sync::Arc;
use crate::{action::Action, resource::ResourceRef, subject::Subject};
pub type AttributePredicate = Arc<dyn Fn(&Subject, &ResourceRef, &Action) -> bool + Send + Sync>;
#[must_use]
#[derive(Clone)]
pub struct AttributeGuard {
predicate: AttributePredicate,
}
impl fmt::Debug for AttributeGuard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AttributeGuard").finish_non_exhaustive()
}
}
impl AttributeGuard {
pub fn new<F>(predicate: F) -> Self
where
F: Fn(&Subject, &ResourceRef, &Action) -> bool + Send + Sync + 'static,
{
Self {
predicate: Arc::new(predicate),
}
}
pub fn allow_all() -> Self {
Self::new(|_, _, _| true)
}
pub fn require_subject_attr(key: impl Into<String>, expected: impl Into<String>) -> Self {
let key = key.into();
let expected = expected.into();
Self::new(move |subject, _, _| subject.attr(&key) == Some(expected.as_str()))
}
pub fn require_resource_attr(key: impl Into<String>, expected: impl Into<String>) -> Self {
let key = key.into();
let expected = expected.into();
Self::new(move |_, resource, _| resource.attr(&key) == Some(expected.as_str()))
}
#[must_use]
pub fn check(&self, subject: &Subject, resource: &ResourceRef, action: &Action) -> bool {
(self.predicate)(subject, resource, action)
}
pub fn and(self, other: Self) -> Self {
Self::new(move |subject, resource, action| {
self.check(subject, resource, action) && other.check(subject, resource, action)
})
}
pub fn or(self, other: Self) -> Self {
Self::new(move |subject, resource, action| {
self.check(subject, resource, action) || other.check(subject, resource, action)
})
}
}