#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
pub mod err;
mod symcc;
mod symccopt;
use cedar_policy::{Policy, PolicySet, RequestEnv, Schema};
use std::fmt;
use err::{Error, Result};
use solver::Solver;
use symcc::{
verify_always_allows, verify_always_denies, verify_always_matches, verify_disjoint,
verify_equivalent, verify_implies, verify_never_errors, verify_never_matches,
well_typed_policies, well_typed_policy, Environment, SymCompiler,
};
pub use symcc::bitvec;
pub use symcc::ext;
pub use symcc::extension_types;
pub use symcc::factory as term_factory;
pub use symcc::op;
pub use symcc::solver;
pub use symcc::term;
pub use symcc::term_type;
pub use symcc::type_abbrevs;
pub use symcc::verifier::Asserts;
pub use symcc::Interpretation;
pub use symcc::{Env, SmtLibScript, SymEnv};
impl SymEnv {
pub fn new(schema: &Schema, req_env: &RequestEnv) -> Result<Self> {
let env = Environment::from_request_env(req_env, schema.as_ref())
.ok_or_else(|| Error::ActionNotInSchema(req_env.action().to_string()))?;
Ok(Self::of_env(&env)?)
}
}
#[derive(Clone, Debug)]
pub struct WellTypedPolicy {
policy: cedar_policy_core::ast::Policy,
}
impl WellTypedPolicy {
pub fn policy(&self) -> &cedar_policy_core::ast::Policy {
&self.policy
}
pub fn from_policy(
policy: &Policy,
env: &RequestEnv,
schema: &Schema,
) -> Result<WellTypedPolicy> {
well_typed_policy(policy.as_ref(), env, schema).map(|p| WellTypedPolicy { policy: p })
}
pub fn from_policy_unchecked(policy: &Policy) -> Self {
WellTypedPolicy {
policy: policy.as_ref().clone(),
}
}
}
impl fmt::Display for WellTypedPolicy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.policy)
}
}
#[derive(Clone, Debug)]
pub struct WellTypedPolicies {
policies: cedar_policy_core::ast::PolicySet,
}
impl WellTypedPolicies {
pub fn policy_set(&self) -> &cedar_policy_core::ast::PolicySet {
&self.policies
}
pub fn from_policies(
ps: &PolicySet,
env: &RequestEnv,
schema: &Schema,
) -> Result<WellTypedPolicies> {
well_typed_policies(ps.as_ref(), env, schema).map(|ps| WellTypedPolicies { policies: ps })
}
pub fn from_policies_unchecked(ps: &PolicySet) -> Self {
WellTypedPolicies {
policies: ps.as_ref().clone(),
}
}
}
impl fmt::Display for WellTypedPolicies {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.policies)
}
}
#[derive(Debug, Clone)]
pub struct CompiledPolicy {
policy: symccopt::CompiledPolicy,
}
impl CompiledPolicy {
pub fn compile(policy: &Policy, env: &RequestEnv, schema: &Schema) -> Result<Self> {
Ok(Self {
policy: symccopt::CompiledPolicy::compile(policy.as_ref(), env, schema)?,
})
}
pub fn into_compiled_policies(self) -> CompiledPolicies {
CompiledPolicies {
policies: self.policy.into_compiled_policies(),
}
}
}
#[derive(Debug, Clone)]
pub struct CompiledPolicies {
policies: symccopt::CompiledPolicies,
}
impl CompiledPolicies {
pub fn compile(pset: &PolicySet, env: &RequestEnv, schema: &Schema) -> Result<Self> {
Ok(Self {
policies: symccopt::CompiledPolicies::compile(pset.as_ref(), env, schema)?,
})
}
}
#[derive(Clone, Debug)]
pub struct CedarSymCompiler<S: Solver> {
symcc: SymCompiler<S>,
}
impl<S: Solver> CedarSymCompiler<S> {
pub fn new(solver: S) -> Result<Self> {
Ok(Self {
symcc: SymCompiler::new(solver),
})
}
pub fn solver(&self) -> &S {
self.symcc.solver()
}
pub fn solver_mut(&mut self) -> &mut S {
self.symcc.solver_mut()
}
pub async fn check_unsat(&mut self, asserts: &WellFormedAsserts<'_>) -> Result<bool> {
self.symcc
.check_unsat(|_| Ok(asserts.asserts().clone()), asserts.symenv())
.await
}
pub async fn check_sat(&mut self, asserts: &WellFormedAsserts<'_>) -> Result<Option<Env>> {
self.symcc
.check_sat_asserts(
asserts.asserts(),
asserts.symenv(),
asserts.footprint.iter(),
)
.await
}
pub async fn check_never_errors(
&mut self,
policy: &WellTypedPolicy,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc.check_never_errors(&policy.policy, symenv).await
}
pub async fn check_never_errors_opt(&mut self, policy: &CompiledPolicy) -> Result<bool> {
self.symcc.check_never_errors_opt(&policy.policy).await
}
pub async fn check_never_errors_with_counterexample(
&mut self,
policy: &WellTypedPolicy,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_never_errors_with_counterexample(&policy.policy, symenv)
.await
}
pub async fn check_never_errors_with_counterexample_opt(
&mut self,
policy: &CompiledPolicy,
) -> Result<Option<Env>> {
self.symcc
.check_never_errors_with_counterexample_opt(&policy.policy)
.await
}
pub async fn check_always_matches(
&mut self,
policy: &WellTypedPolicy,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc
.check_always_matches(&policy.policy, symenv)
.await
}
pub async fn check_always_matches_opt(&mut self, policy: &CompiledPolicy) -> Result<bool> {
self.symcc.check_always_matches_opt(&policy.policy).await
}
pub async fn check_always_matches_with_counterexample(
&mut self,
policy: &WellTypedPolicy,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_always_matches_with_counterexample(&policy.policy, symenv)
.await
}
pub async fn check_always_matches_with_counterexample_opt(
&mut self,
policy: &CompiledPolicy,
) -> Result<Option<Env>> {
self.symcc
.check_always_matches_with_counterexample_opt(&policy.policy)
.await
}
pub async fn check_never_matches(
&mut self,
policy: &WellTypedPolicy,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc.check_never_matches(&policy.policy, symenv).await
}
pub async fn check_never_matches_opt(&mut self, policy: &CompiledPolicy) -> Result<bool> {
self.symcc.check_never_matches_opt(&policy.policy).await
}
pub async fn check_never_matches_with_counterexample(
&mut self,
policy: &WellTypedPolicy,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_never_matches_with_counterexample(&policy.policy, symenv)
.await
}
pub async fn check_never_matches_with_counterexample_opt(
&mut self,
policy: &CompiledPolicy,
) -> Result<Option<Env>> {
self.symcc
.check_never_matches_with_counterexample_opt(&policy.policy)
.await
}
pub async fn check_implies(
&mut self,
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc
.check_implies(&pset1.policies, &pset2.policies, symenv)
.await
}
pub async fn check_implies_opt(
&mut self,
pset1: &CompiledPolicies,
pset2: &CompiledPolicies,
) -> Result<bool> {
self.symcc
.check_implies_opt(&pset1.policies, &pset2.policies)
.await
}
pub async fn check_implies_with_counterexample(
&mut self,
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_implies_with_counterexample(&pset1.policies, &pset2.policies, symenv)
.await
}
pub async fn check_implies_with_counterexample_opt(
&mut self,
pset1: &CompiledPolicies,
pset2: &CompiledPolicies,
) -> Result<Option<Env>> {
self.symcc
.check_implies_with_counterexample_opt(&pset1.policies, &pset2.policies)
.await
}
pub async fn check_always_allows(
&mut self,
pset: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc.check_always_allows(&pset.policies, symenv).await
}
pub async fn check_always_allows_opt(&mut self, pset: &CompiledPolicies) -> Result<bool> {
self.symcc.check_always_allows_opt(&pset.policies).await
}
pub async fn check_always_allows_with_counterexample(
&mut self,
pset: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_always_allows_with_counterexample(&pset.policies, symenv)
.await
}
pub async fn check_always_allows_with_counterexample_opt(
&mut self,
pset: &CompiledPolicies,
) -> Result<Option<Env>> {
self.symcc
.check_always_allows_with_counterexample_opt(&pset.policies)
.await
}
pub async fn check_always_denies(
&mut self,
pset: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc.check_always_denies(&pset.policies, symenv).await
}
pub async fn check_always_denies_opt(&mut self, pset: &CompiledPolicies) -> Result<bool> {
self.symcc.check_always_denies_opt(&pset.policies).await
}
pub async fn check_always_denies_with_counterexample(
&mut self,
pset: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_always_denies_with_counterexample(&pset.policies, symenv)
.await
}
pub async fn check_always_denies_with_counterexample_opt(
&mut self,
pset: &CompiledPolicies,
) -> Result<Option<Env>> {
self.symcc
.check_always_denies_with_counterexample_opt(&pset.policies)
.await
}
pub async fn check_equivalent(
&mut self,
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc
.check_equivalent(&pset1.policies, &pset2.policies, symenv)
.await
}
pub async fn check_equivalent_opt(
&mut self,
pset1: &CompiledPolicies,
pset2: &CompiledPolicies,
) -> Result<bool> {
self.symcc
.check_equivalent_opt(&pset1.policies, &pset2.policies)
.await
}
pub async fn check_equivalent_with_counterexample(
&mut self,
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_equivalent_with_counterexample(&pset1.policies, &pset2.policies, symenv)
.await
}
pub async fn check_equivalent_with_counterexample_opt(
&mut self,
pset1: &CompiledPolicies,
pset2: &CompiledPolicies,
) -> Result<Option<Env>> {
self.symcc
.check_equivalent_with_counterexample_opt(&pset1.policies, &pset2.policies)
.await
}
pub async fn check_disjoint(
&mut self,
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<bool> {
self.symcc
.check_disjoint(&pset1.policies, &pset2.policies, symenv)
.await
}
pub async fn check_disjoint_opt(
&mut self,
pset1: &CompiledPolicies,
pset2: &CompiledPolicies,
) -> Result<bool> {
self.symcc
.check_disjoint_opt(&pset1.policies, &pset2.policies)
.await
}
pub async fn check_disjoint_with_counterexample(
&mut self,
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &SymEnv,
) -> Result<Option<Env>> {
self.symcc
.check_disjoint_with_counterexample(&pset1.policies, &pset2.policies, symenv)
.await
}
pub async fn check_disjoint_with_counterexample_opt(
&mut self,
pset1: &CompiledPolicies,
pset2: &CompiledPolicies,
) -> Result<Option<Env>> {
self.symcc
.check_disjoint_with_counterexample_opt(&pset1.policies, &pset2.policies)
.await
}
}
#[derive(Clone, Debug)]
pub struct WellFormedAsserts<'a> {
symenv: &'a SymEnv,
asserts: Asserts,
footprint: Vec<cedar_policy_core::ast::Expr>,
}
impl<'a> WellFormedAsserts<'a> {
pub fn symenv(&self) -> &SymEnv {
self.symenv
}
pub fn asserts(&self) -> &Asserts {
&self.asserts
}
pub fn from_asserts_unchecked<'b>(
symenv: &'a SymEnv,
asserts: Asserts,
policies: impl Iterator<Item = &'b cedar_policy_core::ast::Policy>,
) -> Self {
WellFormedAsserts {
symenv,
asserts,
footprint: policies.map(|p| p.condition()).collect(),
}
}
}
pub fn compile_never_errors<'a>(
policy: &WellTypedPolicy,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_never_errors(policy.policy(), symenv)?,
std::iter::once(policy.policy()),
))
}
pub fn compile_always_matches<'a>(
policy: &WellTypedPolicy,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_always_matches(policy.policy(), symenv)?,
std::iter::once(policy.policy()),
))
}
pub fn compile_never_matches<'a>(
policy: &WellTypedPolicy,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_never_matches(policy.policy(), symenv)?,
std::iter::once(policy.policy()),
))
}
pub fn compile_implies<'a>(
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_implies(pset1.policy_set(), pset2.policy_set(), symenv)?,
pset1
.policy_set()
.policies()
.chain(pset2.policy_set().policies()),
))
}
pub fn compile_always_allows<'a>(
pset: &WellTypedPolicies,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_always_allows(pset.policy_set(), symenv)?,
pset.policy_set().policies(),
))
}
pub fn compile_always_denies<'a>(
pset: &WellTypedPolicies,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_always_denies(pset.policy_set(), symenv)?,
pset.policy_set().policies(),
))
}
pub fn compile_equivalent<'a>(
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_equivalent(pset1.policy_set(), pset2.policy_set(), symenv)?,
pset1
.policy_set()
.policies()
.chain(pset2.policy_set().policies()),
))
}
pub fn compile_disjoint<'a>(
pset1: &WellTypedPolicies,
pset2: &WellTypedPolicies,
symenv: &'a SymEnv,
) -> Result<WellFormedAsserts<'a>> {
Ok(WellFormedAsserts::from_asserts_unchecked(
symenv,
verify_disjoint(pset1.policy_set(), pset2.policy_set(), symenv)?,
pset1
.policy_set()
.policies()
.chain(pset2.policy_set().policies()),
))
}