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.into_iter().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,
policies: PolicySet::singleton(self.policy),
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.into_iter().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,
}
}
}
#[cfg(test)]
mod test {
use cedar_policy::{RequestEnv, Schema};
use cedar_policy_core::{
ast::{Policy, PolicySet},
parser::parse_policy,
};
use super::{CompiledPolicy, CompiledPolicySet};
#[track_caller]
fn assert_compile_into_policies_eq_compile_policies(
p: &str,
schema: &Schema,
env: &RequestEnv,
) {
let p: Policy = parse_policy(None, p).unwrap().into();
assert_eq!(
CompiledPolicy::compile(&p, &env, &schema)
.unwrap()
.into_compiled_policyset(),
CompiledPolicySet::compile(&PolicySet::singleton(p), &env, &schema).unwrap()
);
}
#[test]
fn compile_into_policies_eq_compile_policies() {
let schema = Schema::from_cedarschema_str(
"entity E; action A appliesTo { principal: E, resource: E};",
)
.unwrap()
.0;
let env = RequestEnv::new(
"E".parse().unwrap(),
r#"Action::"A""#.parse().unwrap(),
"E".parse().unwrap(),
);
assert_compile_into_policies_eq_compile_policies(
"permit(principal, action, resource);",
&schema,
&env,
);
assert_compile_into_policies_eq_compile_policies(
r#"permit(principal == E::"e", action, resource);"#,
&schema,
&env,
);
assert_compile_into_policies_eq_compile_policies(
"forbid(principal, action, resource);",
&schema,
&env,
);
assert_compile_into_policies_eq_compile_policies(
r#"forbid(principal == E::"e", action, resource);"#,
&schema,
&env,
);
}
}