1use super::*;
2
3pub const DEFAULT_EXPLAIN_MAX_DEPTH: usize = 100;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct ExplainOptions {
7 pub max_depth: usize,
8 pub cycle_protection: bool,
9}
10
11impl ExplainOptions {
12 pub const fn new(max_depth: usize) -> Self {
13 Self {
14 max_depth,
15 cycle_protection: true,
16 }
17 }
18
19 pub const fn with_cycle_protection(mut self, cycle_protection: bool) -> Self {
20 self.cycle_protection = cycle_protection;
21 self
22 }
23
24 pub const fn normalized(self) -> Self {
25 let max_depth = if self.max_depth == 0 {
26 1
27 } else {
28 self.max_depth
29 };
30
31 Self {
32 max_depth,
33 cycle_protection: self.cycle_protection,
34 }
35 }
36}
37
38impl Default for ExplainOptions {
39 fn default() -> Self {
40 Self {
41 max_depth: DEFAULT_EXPLAIN_MAX_DEPTH,
42 cycle_protection: true,
43 }
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum ExplainDecision {
49 Allow,
50 Deny,
51}
52
53impl ExplainDecision {
54 pub const fn is_allowed(self) -> bool {
55 matches!(self, Self::Allow)
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Hash)]
60pub struct ExplainedNode {
61 pub object: Entity,
62 pub relation: Option<Relation>,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
66pub enum ExplainStep {
67 Start {
68 node: ExplainedNode,
69 },
70 DirectSubjectMatch {
71 node: ExplainedNode,
72 },
73 TupleSubjectMatch {
74 from: ExplainedNode,
75 tuple: DefaultTuple,
76 },
77 Inherit {
78 from: ExplainedNode,
79 to: ExplainedNode,
80 },
81 TupleTraversal {
82 from: ExplainedNode,
83 tuple: DefaultTuple,
84 to: ExplainedNode,
85 },
86 Computed {
87 from: ExplainedNode,
88 via_tuple: DefaultTuple,
89 to: ExplainedNode,
90 },
91 TupleToUserset {
92 from: ExplainedNode,
93 via_tuple: DefaultTuple,
94 to: ExplainedNode,
95 },
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum DeniedReason {
100 NoMatchingPath,
101 RecursionLimitReached { max_depth: usize },
102 CycleDetected,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub enum DeniedAttempt {
107 Inherit {
108 step: ExplainStep,
109 result: Box<DeniedExplanation>,
110 },
111 TupleTraversal {
112 step: ExplainStep,
113 result: Box<DeniedExplanation>,
114 },
115 Computed {
116 step: ExplainStep,
117 result: Box<DeniedExplanation>,
118 },
119 TupleToUserset {
120 step: ExplainStep,
121 result: Box<DeniedExplanation>,
122 },
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct DeniedExplanation {
127 pub node: ExplainedNode,
128 pub reason: DeniedReason,
129 pub attempts: Vec<DeniedAttempt>,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
133pub struct AllowedExplanation {
134 pub steps: Vec<ExplainStep>,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub enum ExplainTrace {
139 Allowed(AllowedExplanation),
140 Denied(DeniedExplanation),
141}
142
143#[derive(Debug, Clone, PartialEq, Eq)]
144pub struct CapabilityExplanation {
145 pub manifest: AuthModelManifest,
146 pub subject: DefaultSubject,
147 pub capability: Capability,
148 pub object: Entity,
149 pub binding: CapabilityBinding,
150 pub decision: ExplainDecision,
151 pub options: ExplainOptions,
152 pub trace: ExplainTrace,
153}