use crate::{EvaluationSession, FactProvenance, PolicyEvalResult, SecurityRuleMetadata};
use async_trait::async_trait;
use std::borrow::Cow;
use std::sync::Arc;
pub trait PolicyDomain: Send + Sync + 'static {
type Subject: Send + Sync;
type Action: Send + Sync;
type Resource: Send + Sync;
type Context: Send + Sync;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Effect {
Allow,
Forbid,
AllowOrForbid,
}
impl Effect {
pub fn can_grant(self) -> bool {
matches!(self, Self::Allow | Self::AllowOrForbid)
}
pub fn can_forbid(self) -> bool {
matches!(self, Self::Forbid | Self::AllowOrForbid)
}
pub(crate) fn from_capabilities(can_grant: bool, can_forbid: bool) -> Self {
match (can_grant, can_forbid) {
(true, true) => Self::AllowOrForbid,
(false, true) => Self::Forbid,
_ => Self::Allow,
}
}
pub(crate) fn telemetry_label(self) -> &'static str {
match self {
Self::Allow => "allow",
Self::Forbid => "deny",
Self::AllowOrForbid => "allow_or_forbid",
}
}
}
pub struct PolicyBatchItem<'a, D: PolicyDomain> {
pub resource: &'a D::Resource,
}
pub struct EvalCtx<'a, D: PolicyDomain> {
pub session: &'a EvaluationSession,
pub subject: &'a D::Subject,
pub action: &'a D::Action,
pub resource: &'a D::Resource,
pub context: &'a D::Context,
pub policy_type: Cow<'static, str>,
}
impl<'a, D: PolicyDomain> EvalCtx<'a, D> {
pub fn grant(&self, reason: impl Into<String>) -> PolicyEvalResult {
PolicyEvalResult::granted(self.policy_type.clone(), Some(reason.into()))
}
pub fn not_applicable(&self, reason: impl Into<String>) -> PolicyEvalResult {
PolicyEvalResult::not_applicable(self.policy_type.clone(), reason)
}
pub fn forbid(&self, reason: impl Into<String>) -> PolicyEvalResult {
PolicyEvalResult::forbidden(self.policy_type.clone(), reason)
}
pub fn grant_with_facts(
&self,
reason: impl Into<String>,
provenance: Vec<FactProvenance>,
) -> PolicyEvalResult {
PolicyEvalResult::granted_with_facts(
self.policy_type.clone(),
Some(reason.into()),
provenance,
)
}
pub fn not_applicable_with_facts(
&self,
reason: impl Into<String>,
provenance: Vec<FactProvenance>,
) -> PolicyEvalResult {
PolicyEvalResult::not_applicable_with_facts(self.policy_type.clone(), reason, provenance)
}
pub fn forbid_with_facts(
&self,
reason: impl Into<String>,
provenance: Vec<FactProvenance>,
) -> PolicyEvalResult {
PolicyEvalResult::forbidden_with_facts(self.policy_type.clone(), reason, provenance)
}
}
pub struct BatchEvalCtx<'a, D: PolicyDomain> {
pub session: &'a EvaluationSession,
pub subject: &'a D::Subject,
pub action: &'a D::Action,
pub context: &'a D::Context,
pub items: &'a [PolicyBatchItem<'a, D>],
pub policy_type: Cow<'static, str>,
}
#[async_trait]
pub trait Policy<D: PolicyDomain>: Send + Sync {
async fn evaluate(&self, ctx: &EvalCtx<'_, D>) -> PolicyEvalResult;
async fn evaluate_batch<'item>(&self, ctx: &BatchEvalCtx<'item, D>) -> Vec<PolicyEvalResult> {
let mut results = Vec::with_capacity(ctx.items.len());
for item in ctx.items {
let item_ctx = EvalCtx {
session: ctx.session,
subject: ctx.subject,
action: ctx.action,
resource: item.resource,
context: ctx.context,
policy_type: ctx.policy_type.clone(),
};
results.push(self.evaluate(&item_ctx).await);
}
results
}
fn policy_type(&self) -> Cow<'static, str>;
fn effect(&self) -> Effect {
Effect::Allow
}
fn security_rule(&self) -> SecurityRuleMetadata {
SecurityRuleMetadata::default()
}
}
#[async_trait]
impl<D> Policy<D> for Box<dyn Policy<D>>
where
D: PolicyDomain,
{
async fn evaluate(&self, ctx: &EvalCtx<'_, D>) -> PolicyEvalResult {
(**self).evaluate(ctx).await
}
async fn evaluate_batch<'item>(&self, ctx: &BatchEvalCtx<'item, D>) -> Vec<PolicyEvalResult> {
(**self).evaluate_batch(ctx).await
}
fn policy_type(&self) -> Cow<'static, str> {
(**self).policy_type()
}
fn effect(&self) -> Effect {
(**self).effect()
}
fn security_rule(&self) -> SecurityRuleMetadata {
(**self).security_rule()
}
}
#[async_trait]
impl<D> Policy<D> for Arc<dyn Policy<D>>
where
D: PolicyDomain,
{
async fn evaluate(&self, ctx: &EvalCtx<'_, D>) -> PolicyEvalResult {
(**self).evaluate(ctx).await
}
async fn evaluate_batch<'item>(&self, ctx: &BatchEvalCtx<'item, D>) -> Vec<PolicyEvalResult> {
(**self).evaluate_batch(ctx).await
}
fn policy_type(&self) -> Cow<'static, str> {
(**self).policy_type()
}
fn effect(&self) -> Effect {
(**self).effect()
}
fn security_rule(&self) -> SecurityRuleMetadata {
(**self).security_rule()
}
}