use std::collections::BTreeSet;
use cedar_policy::{Effect, RequestEnv, Schema};
use cedar_policy_core::ast::{Policy, PolicySet};
use crate::symcc::{self, factory, term::Term, SymEnv};
use crate::symccopt::{self, compiler::CompileResult};
use crate::Result;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompiledPolicy {
pub(crate) term: Term,
pub(crate) symenv: symcc::SymEnv,
pub(crate) policy: Policy,
pub(crate) footprint: BTreeSet<Term>,
pub(crate) acyclicity: BTreeSet<Term>,
}
impl CompiledPolicy {
pub fn compile(policy: &Policy, env: &RequestEnv, schema: &Schema) -> Result<Self> {
Self::compile_with_custom_symenv(policy, env, schema, SymEnv::new(schema, env)?)
}
pub fn compile_with_custom_symenv(
policy: &Policy,
env: &RequestEnv,
schema: &Schema,
symenv: SymEnv,
) -> Result<Self> {
let policy = symcc::well_typed_policy(policy, env, schema)?;
let CompileResult { term, footprint } =
symccopt::compiler::compile(&policy.condition(), &symenv)?;
let footprint: BTreeSet<Term> = footprint.collect(); let acyclicity = footprint
.iter()
.map(|term| symcc::enforcer::acyclicity(term, &symenv.entities))
.collect();
Ok(Self {
term,
symenv,
policy,
footprint,
acyclicity,
})
}
pub fn effect(&self) -> Effect {
self.policy.effect()
}
pub fn into_compiled_policyset(self) -> CompiledPolicySet {
CompiledPolicySet {
term: match self.policy.effect() {
Effect::Forbid => {
false.into()
}
Effect::Permit => {
factory::eq(self.term, factory::some_of(true.into()))
}
},
symenv: self.symenv,
#[expect(
clippy::expect_used,
reason = "Constructing a singleton policyset should not fail. For more future-proof (in case somehow in the future it becomes possible for constructing a singleton policyset to fail), we should add a `PolicySet::singleton()` function to cedar-policy-core and use that here."
)]
policies: PolicySet::try_from_iter([self.policy])
.expect("constructing a singleton policyset should not fail"),
footprint: self.footprint, acyclicity: self.acyclicity, }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompiledPolicySet {
pub(crate) term: Term,
pub(crate) symenv: symcc::SymEnv,
pub(crate) policies: PolicySet,
pub(crate) footprint: BTreeSet<Term>,
pub(crate) acyclicity: BTreeSet<Term>,
}
impl CompiledPolicySet {
pub fn compile(pset: &PolicySet, env: &RequestEnv, schema: &Schema) -> Result<Self> {
Self::compile_with_custom_symenv(pset, env, schema, SymEnv::new(schema, env)?)
}
pub fn compile_with_custom_symenv(
pset: &PolicySet,
env: &RequestEnv,
schema: &Schema,
symenv: SymEnv,
) -> Result<Self> {
let policies = symcc::well_typed_policies(pset, env, schema)?;
let CompileResult { term, footprint } =
symccopt::authorizer::is_authorized(&policies, &symenv)?;
let footprint: BTreeSet<Term> = footprint.collect(); let acyclicity = footprint
.iter()
.map(|term| symcc::enforcer::acyclicity(term, &symenv.entities))
.collect();
Ok(Self {
term,
symenv,
policies,
footprint,
acyclicity,
})
}
pub fn allow_all(symenv: SymEnv) -> Self {
Self {
term: Term::from(true),
policies: symcc::verifier::allow_all_pset(),
footprint: BTreeSet::new(),
acyclicity: BTreeSet::new(),
symenv,
}
}
pub fn deny_all(symenv: SymEnv) -> Self {
Self {
term: Term::from(false),
symenv,
policies: PolicySet::new(),
footprint: BTreeSet::new(),
acyclicity: BTreeSet::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompiledPolicies<'a> {
Policy(&'a CompiledPolicy),
PolicySet(&'a CompiledPolicySet),
}
impl<'a> CompiledPolicies<'a> {
pub fn all_policies<'s>(&'s self) -> Box<dyn Iterator<Item = &'a Policy> + 's> {
match self {
CompiledPolicies::Policy(cp) => Box::new(std::iter::once(&cp.policy)),
CompiledPolicies::PolicySet(cpset) => Box::new(cpset.policies.policies()),
}
}
pub fn footprint(&self) -> &BTreeSet<Term> {
match self {
CompiledPolicies::Policy(cp) => &cp.footprint,
CompiledPolicies::PolicySet(cpset) => &cpset.footprint,
}
}
pub fn symenv(&self) -> &SymEnv {
match self {
CompiledPolicies::Policy(cp) => &cp.symenv,
CompiledPolicies::PolicySet(cpset) => &cpset.symenv,
}
}
}