Skip to main content

actrpc_interceptor/interceptors/policy/config/
config.rs

1use actrpc_core::json_rpc::JsonRpcError;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
5#[serde(deny_unknown_fields)]
6pub struct PolicyConfig {
7    #[serde(default)]
8    pub rules: Vec<PolicyRule>,
9}
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12#[serde(deny_unknown_fields)]
13pub struct PolicyRule {
14    pub name: String,
15    pub match_expr: MatchExpr,
16
17    #[serde(default)]
18    pub apply: PolicyApply,
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
22#[serde(deny_unknown_fields)]
23pub struct PolicyApply {
24    #[serde(default)]
25    pub immediate: Vec<PolicyEffect>,
26
27    #[serde(default)]
28    pub review: Option<PolicyReview>,
29}
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct PolicyReview {
34    pub title: String,
35    pub reason: String,
36    pub severity: PolicyReviewSeverity,
37
38    #[serde(default)]
39    pub on_approve: Vec<PolicyEffect>,
40
41    #[serde(default)]
42    pub on_deny: Vec<PolicyEffect>,
43}
44
45impl PolicyReview {
46    pub fn effective_on_deny(&self) -> Vec<PolicyEffect> {
47        if self.on_deny.is_empty() {
48            vec![PolicyEffect::reject_call(JsonRpcError {
49                code: -32050,
50                message: "operation denied by user review".to_owned(),
51                data: None,
52            })]
53        } else {
54            self.on_deny.clone()
55        }
56    }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(rename_all = "snake_case")]
61pub enum PolicyReviewSeverity {
62    Low,
63    Medium,
64    High,
65}
66
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68#[serde(deny_unknown_fields, untagged)]
69pub enum MatchExpr {
70    All { all: Vec<MatchExpr> },
71    Any { any: Vec<MatchExpr> },
72    Condition { condition: PolicyCondition },
73}
74
75#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
76#[serde(deny_unknown_fields)]
77pub struct PolicyCondition {
78    pub fact: PolicyFactPath,
79    pub matcher: PolicyMatcher,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
83#[serde(transparent)]
84pub struct PolicyFactPath(String);
85
86impl PolicyFactPath {
87    pub fn new(value: impl Into<String>) -> Self {
88        Self(value.into())
89    }
90
91    pub fn as_str(&self) -> &str {
92        &self.0
93    }
94
95    pub fn into_inner(self) -> String {
96        self.0
97    }
98}
99
100impl From<String> for PolicyFactPath {
101    fn from(value: String) -> Self {
102        Self(value)
103    }
104}
105
106impl From<&str> for PolicyFactPath {
107    fn from(value: &str) -> Self {
108        Self(value.to_owned())
109    }
110}
111
112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
113#[serde(deny_unknown_fields)]
114pub struct PolicyMatcher {
115    pub kind: PolicyMatcherKind,
116    pub value: String,
117
118    #[serde(default)]
119    pub negated: bool,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
123#[serde(rename_all = "snake_case")]
124pub enum PolicyMatcherKind {
125    Exact,
126    Glob,
127    Regex,
128}
129
130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
131#[serde(deny_unknown_fields, untagged)]
132pub enum PolicyEffect {
133    ExcludeInterceptors {
134        exclude_interceptors: ExcludeInterceptorsEffect,
135    },
136    RejectCall {
137        reject_call: RejectCallEffect,
138    },
139}
140
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
142#[serde(deny_unknown_fields)]
143pub struct ExcludeInterceptorsEffect {
144    pub names: Vec<String>,
145}
146
147#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
148#[serde(deny_unknown_fields)]
149pub struct RejectCallEffect {
150    pub error: JsonRpcError,
151}
152
153impl PolicyEffect {
154    pub fn exclude_interceptors(names: Vec<String>) -> Self {
155        Self::ExcludeInterceptors {
156            exclude_interceptors: ExcludeInterceptorsEffect { names },
157        }
158    }
159
160    pub fn reject_call(error: JsonRpcError) -> Self {
161        Self::RejectCall {
162            reject_call: RejectCallEffect { error },
163        }
164    }
165}
166
167impl MatchExpr {
168    pub fn all(expressions: impl Into<Vec<MatchExpr>>) -> Self {
169        Self::All {
170            all: expressions.into(),
171        }
172    }
173
174    pub fn any(expressions: impl Into<Vec<MatchExpr>>) -> Self {
175        Self::Any {
176            any: expressions.into(),
177        }
178    }
179
180    pub fn condition(fact: impl Into<PolicyFactPath>, matcher: PolicyMatcher) -> Self {
181        Self::Condition {
182            condition: PolicyCondition {
183                fact: fact.into(),
184                matcher,
185            },
186        }
187    }
188}
189
190impl PolicyMatcher {
191    pub fn exact(value: impl Into<String>) -> Self {
192        Self {
193            kind: PolicyMatcherKind::Exact,
194            value: value.into(),
195            negated: false,
196        }
197    }
198
199    pub fn exact_not(value: impl Into<String>) -> Self {
200        Self {
201            kind: PolicyMatcherKind::Exact,
202            value: value.into(),
203            negated: true,
204        }
205    }
206
207    pub fn glob(value: impl Into<String>) -> Self {
208        Self {
209            kind: PolicyMatcherKind::Glob,
210            value: value.into(),
211            negated: false,
212        }
213    }
214
215    pub fn glob_not(value: impl Into<String>) -> Self {
216        Self {
217            kind: PolicyMatcherKind::Glob,
218            value: value.into(),
219            negated: true,
220        }
221    }
222
223    pub fn regex(value: impl Into<String>) -> Self {
224        Self {
225            kind: PolicyMatcherKind::Regex,
226            value: value.into(),
227            negated: false,
228        }
229    }
230
231    pub fn regex_not(value: impl Into<String>) -> Self {
232        Self {
233            kind: PolicyMatcherKind::Regex,
234            value: value.into(),
235            negated: true,
236        }
237    }
238}