actrpc_interceptor/interceptors/policy/config/
config.rs1use 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}