coil-auth 0.1.0

Authorisation models and auth package support for the Coil framework.
Documentation
use super::*;

pub const DEFAULT_EXPLAIN_MAX_DEPTH: usize = 100;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExplainOptions {
    pub max_depth: usize,
    pub cycle_protection: bool,
}

impl ExplainOptions {
    pub const fn new(max_depth: usize) -> Self {
        Self {
            max_depth,
            cycle_protection: true,
        }
    }

    pub const fn with_cycle_protection(mut self, cycle_protection: bool) -> Self {
        self.cycle_protection = cycle_protection;
        self
    }

    pub const fn normalized(self) -> Self {
        let max_depth = if self.max_depth == 0 {
            1
        } else {
            self.max_depth
        };

        Self {
            max_depth,
            cycle_protection: self.cycle_protection,
        }
    }
}

impl Default for ExplainOptions {
    fn default() -> Self {
        Self {
            max_depth: DEFAULT_EXPLAIN_MAX_DEPTH,
            cycle_protection: true,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExplainDecision {
    Allow,
    Deny,
}

impl ExplainDecision {
    pub const fn is_allowed(self) -> bool {
        matches!(self, Self::Allow)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExplainedNode {
    pub object: Entity,
    pub relation: Option<Relation>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExplainStep {
    Start {
        node: ExplainedNode,
    },
    DirectSubjectMatch {
        node: ExplainedNode,
    },
    TupleSubjectMatch {
        from: ExplainedNode,
        tuple: DefaultTuple,
    },
    Inherit {
        from: ExplainedNode,
        to: ExplainedNode,
    },
    TupleTraversal {
        from: ExplainedNode,
        tuple: DefaultTuple,
        to: ExplainedNode,
    },
    Computed {
        from: ExplainedNode,
        via_tuple: DefaultTuple,
        to: ExplainedNode,
    },
    TupleToUserset {
        from: ExplainedNode,
        via_tuple: DefaultTuple,
        to: ExplainedNode,
    },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeniedReason {
    NoMatchingPath,
    RecursionLimitReached { max_depth: usize },
    CycleDetected,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeniedAttempt {
    Inherit {
        step: ExplainStep,
        result: Box<DeniedExplanation>,
    },
    TupleTraversal {
        step: ExplainStep,
        result: Box<DeniedExplanation>,
    },
    Computed {
        step: ExplainStep,
        result: Box<DeniedExplanation>,
    },
    TupleToUserset {
        step: ExplainStep,
        result: Box<DeniedExplanation>,
    },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeniedExplanation {
    pub node: ExplainedNode,
    pub reason: DeniedReason,
    pub attempts: Vec<DeniedAttempt>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AllowedExplanation {
    pub steps: Vec<ExplainStep>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExplainTrace {
    Allowed(AllowedExplanation),
    Denied(DeniedExplanation),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CapabilityExplanation {
    pub manifest: AuthModelManifest,
    pub subject: DefaultSubject,
    pub capability: Capability,
    pub object: Entity,
    pub binding: CapabilityBinding,
    pub decision: ExplainDecision,
    pub options: ExplainOptions,
    pub trace: ExplainTrace,
}