use std::sync::Arc;
use super::authorizer::is_authorized;
use super::compiler::compile;
use super::enforcer::enforce;
use super::env::SymEnv;
use super::factory::{and, eq, implies, is_some, not, some_of};
use super::result::CompileError;
use super::term::Term;
use cedar_policy::Effect;
use cedar_policy_core::ast::{Expr, Policy, PolicyID, PolicySet};
pub type Asserts = Arc<Vec<Term>>;
type Result<T> = std::result::Result<T, CompileError>;
pub fn verify_evaluate(
phi: impl FnOnce(Term) -> Term,
policy: &Policy,
env: &SymEnv,
) -> Result<Asserts> {
let policy_expr = policy.condition();
let term = compile(&policy_expr, env)?;
Ok(Arc::new(
enforce([&policy_expr], env)
.into_iter()
.chain([not(phi(term))])
.collect(),
))
}
pub fn verify_evaluate_pair(
phi: impl FnOnce(Term, Term) -> Term,
policy1: &Policy,
policy2: &Policy,
env: &SymEnv,
) -> Result<Asserts> {
let policy1_expr = policy1.condition();
let policy2_expr = policy2.condition();
let term1 = compile(&policy1_expr, env)?;
let term2 = compile(&policy2_expr, env)?;
Ok(Arc::new(
enforce([&policy1_expr, &policy2_expr], env)
.into_iter()
.chain([not(phi(term1, term2))])
.collect(),
))
}
pub fn verify_is_authorized(
phi: impl FnOnce(Term, Term) -> Term,
policies1: &PolicySet,
policies2: &PolicySet,
env: &SymEnv,
) -> Result<Asserts> {
let term1 = is_authorized(policies1, env)?;
let term2 = is_authorized(policies2, env)?;
let xs: Vec<Expr> = policies1
.policies()
.chain(policies2.policies())
.map(|p| p.condition())
.collect();
Ok(Arc::new(
enforce(xs.iter(), env)
.into_iter()
.chain([not(phi(term1, term2))])
.collect(),
))
}
pub fn verify_never_errors(policy: &Policy, env: &SymEnv) -> Result<Asserts> {
verify_evaluate(is_some, policy, env)
}
pub fn verify_always_matches(policy: &Policy, env: &SymEnv) -> Result<Asserts> {
verify_evaluate(|term| eq(term, some_of(true.into())), policy, env)
}
pub fn verify_never_matches(policy: &Policy, env: &SymEnv) -> Result<Asserts> {
verify_evaluate(|term| not(eq(term, some_of(true.into()))), policy, env)
}
pub fn verify_matches_equivalent(
policy1: &Policy,
policy2: &Policy,
env: &SymEnv,
) -> Result<Asserts> {
verify_evaluate_pair(
|term1, term2| {
let t1matches = eq(term1, some_of(true.into()));
let t2matches = eq(term2, some_of(true.into()));
eq(t1matches, t2matches)
},
policy1,
policy2,
env,
)
}
pub fn verify_matches_implies(policy1: &Policy, policy2: &Policy, env: &SymEnv) -> Result<Asserts> {
verify_evaluate_pair(
|term1, term2| {
let t1matches = eq(term1, some_of(true.into()));
let t2matches = eq(term2, some_of(true.into()));
implies(t1matches, t2matches)
},
policy1,
policy2,
env,
)
}
pub fn verify_matches_disjoint(
policy1: &Policy,
policy2: &Policy,
env: &SymEnv,
) -> Result<Asserts> {
let disjoint = |t1: Term, t2: Term| not(and(t1, t2));
verify_evaluate_pair(
|term1, term2| {
let t1matches = eq(term1, some_of(true.into()));
let t2matches = eq(term2, some_of(true.into()));
disjoint(t1matches, t2matches)
},
policy1,
policy2,
env,
)
}
pub fn verify_implies(
policies1: &PolicySet,
policies2: &PolicySet,
env: &SymEnv,
) -> Result<Asserts> {
verify_is_authorized(implies, policies1, policies2, env)
}
pub(crate) fn allow_all() -> Policy {
Policy::from_when_clause(
Effect::Permit,
Expr::and(
Expr::val(true),
Expr::and(Expr::val(true), Expr::and(Expr::val(true), Expr::val(true))),
),
PolicyID::from_string("allowAll"),
None,
)
}
pub(crate) fn allow_all_pset() -> PolicySet {
PolicySet::singleton(allow_all())
}
pub fn verify_always_allows(policies: &PolicySet, env: &SymEnv) -> Result<Asserts> {
verify_implies(&allow_all_pset(), policies, env)
}
pub fn verify_always_denies(policies: &PolicySet, env: &SymEnv) -> Result<Asserts> {
verify_implies(policies, &PolicySet::new(), env)
}
pub fn verify_equivalent(
policies1: &PolicySet,
policies2: &PolicySet,
env: &SymEnv,
) -> Result<Asserts> {
verify_is_authorized(eq, policies1, policies2, env)
}
pub fn verify_disjoint(
policies1: &PolicySet,
policies2: &PolicySet,
env: &SymEnv,
) -> Result<Asserts> {
let disjoint = |t1: Term, t2: Term| not(and(t1, t2));
verify_is_authorized(disjoint, policies1, policies2, env)
}