use async_trait::async_trait;
use gatehouse::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use uuid::Uuid;
#[derive(Debug, Clone)]
struct User {
id: Uuid,
}
impl User {
fn new() -> Self {
Self { id: Uuid::new_v4() }
}
}
#[derive(Debug, Clone)]
struct Document {
id: Uuid,
}
impl Document {
fn new() -> Self {
Self { id: Uuid::new_v4() }
}
}
#[derive(Debug, Clone)]
struct ViewAction;
struct DocumentDomain;
impl PolicyDomain for DocumentDomain {
type Subject = User;
type Action = ViewAction;
type Resource = Document;
type Context = ();
}
struct CountingPolicy {
allow: bool,
name: String,
counter: Arc<AtomicUsize>,
}
#[async_trait]
impl Policy<DocumentDomain> for CountingPolicy {
async fn evaluate(&self, ctx: &EvalCtx<'_, DocumentDomain>) -> PolicyEvalResult {
self.counter.fetch_add(1, Ordering::SeqCst);
println!("Evaluating policy: {}", self.name);
if self.allow {
ctx.grant(format!("{} grants access", self.name))
} else {
ctx.not_applicable(format!("{} denies access", self.name))
}
}
fn policy_type(&self) -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Owned(self.name.clone())
}
}
#[tokio::main]
async fn main() {
let user = User::new();
let document = Document::new();
let action = ViewAction;
let context = ();
println!("=== AND Policy Short-Circuit Example ===");
{
let counter = Arc::new(AtomicUsize::new(0));
let and_policy = CountingPolicy {
allow: false,
name: "DenyFirst".to_string(),
counter: counter.clone(),
}
.and(CountingPolicy {
allow: true,
name: "AllowSecond".to_string(),
counter: counter.clone(),
});
let mut checker = PermissionChecker::<DocumentDomain>::new();
checker.add_policy(and_policy);
println!("Evaluating AND(DenyFirst, AllowSecond):");
let session = EvaluationSession::empty();
let result = checker
.bind(&session, &user, &action, &context)
.check(&document)
.await;
println!(
"Result: {}",
if result.is_granted() {
"Access granted"
} else {
"Access denied"
}
);
println!("Policies evaluated: {}", counter.load(Ordering::SeqCst));
println!("Trace:\n{}", result.trace().format());
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
println!("\n=== OR Policy Short-Circuit Example ===");
{
let counter = Arc::new(AtomicUsize::new(0));
let or_policy = CountingPolicy {
allow: true,
name: "AllowFirst".to_string(),
counter: counter.clone(),
}
.or(CountingPolicy {
allow: false,
name: "DenySecond".to_string(),
counter: counter.clone(),
});
let mut checker = PermissionChecker::<DocumentDomain>::new();
checker.add_policy(or_policy);
println!("Evaluating OR(AllowFirst, DenySecond):");
let session = EvaluationSession::empty();
let result = checker
.bind(&session, &user, &action, &context)
.check(&document)
.await;
println!(
"Result: {}",
if result.is_granted() {
"Access granted"
} else {
"Access denied"
}
);
println!("Policies evaluated: {}", counter.load(Ordering::SeqCst));
println!("Trace:\n{}", result.trace().format());
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
println!("\n=== Complex Nested Policy Example ===");
{
let counter = Arc::new(AtomicUsize::new(0));
let inner_and = CountingPolicy {
allow: false,
name: "DenyInner".to_string(),
counter: counter.clone(),
}
.and(CountingPolicy {
allow: true,
name: "AllowInner".to_string(),
counter: counter.clone(),
});
let complex_policy = inner_and.or(CountingPolicy {
allow: true,
name: "AllowOuter".to_string(),
counter: counter.clone(),
});
let mut checker = PermissionChecker::<DocumentDomain>::new();
checker.add_policy(complex_policy);
println!("Evaluating OR(AND(DenyInner, AllowInner), AllowOuter):");
let session = EvaluationSession::empty();
let result = checker
.bind(&session, &user, &action, &context)
.check(&document)
.await;
println!(
"Result: {} for document with ID {} for user with ID {}",
if result.is_granted() {
"Access granted"
} else {
"Access denied"
},
document.id,
user.id
);
println!("Policies evaluated: {}", counter.load(Ordering::SeqCst));
println!("Trace:\n{}", result.trace().format());
assert_eq!(counter.load(Ordering::SeqCst), 2);
}
}