1use alloc::string::String;
4use alloc::vec::Vec;
5
6#[cfg(feature = "schemars")]
7use schemars::JsonSchema;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(feature = "schemars", derive(JsonSchema))]
15#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
16pub enum Protocol {
17 Http,
19 Https,
21 Tcp,
23 Udp,
25 Grpc,
27 Custom(String),
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[cfg_attr(feature = "schemars", derive(JsonSchema))]
35pub struct AllowList {
36 #[cfg_attr(
38 feature = "serde",
39 serde(default, skip_serializing_if = "Vec::is_empty")
40 )]
41 pub domains: Vec<String>,
42 #[cfg_attr(
44 feature = "serde",
45 serde(default, skip_serializing_if = "Vec::is_empty")
46 )]
47 pub ports: Vec<u16>,
48 #[cfg_attr(
50 feature = "serde",
51 serde(default, skip_serializing_if = "Vec::is_empty")
52 )]
53 pub protocols: Vec<Protocol>,
54}
55
56impl AllowList {
57 pub fn empty() -> Self {
59 Self {
60 domains: Vec::new(),
61 ports: Vec::new(),
62 protocols: Vec::new(),
63 }
64 }
65
66 pub fn is_empty(&self) -> bool {
68 self.domains.is_empty() && self.ports.is_empty() && self.protocols.is_empty()
69 }
70}
71
72impl Default for AllowList {
73 fn default() -> Self {
74 Self::empty()
75 }
76}
77
78#[derive(Clone, Debug, PartialEq, Eq, Default)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81#[cfg_attr(feature = "schemars", derive(JsonSchema))]
82pub struct NetworkPolicy {
83 pub egress: AllowList,
85 pub deny_on_miss: bool,
87}
88
89impl NetworkPolicy {
90 pub fn strict(egress: AllowList) -> Self {
92 Self {
93 egress,
94 deny_on_miss: true,
95 }
96 }
97}
98
99#[derive(Clone, Debug, PartialEq, Eq)]
101#[cfg_attr(feature = "schemars", derive(JsonSchema))]
102pub struct PolicyDecision {
103 pub status: PolicyDecisionStatus,
105 pub reasons: Vec<String>,
107 pub allow: Option<bool>,
109 pub reason: Option<String>,
111}
112
113#[derive(Clone, Debug, PartialEq, Eq, Hash)]
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
117#[cfg_attr(feature = "schemars", derive(JsonSchema))]
118pub enum PolicyDecisionStatus {
119 Allow,
121 Deny,
123}
124
125#[cfg(feature = "serde")]
126mod serde_impls {
127 use super::{PolicyDecision, PolicyDecisionStatus};
128 use alloc::vec::Vec;
129 use serde::de::{self, MapAccess, Visitor};
130 use serde::ser::SerializeStruct;
131 use serde::{Deserialize, Deserializer, Serialize, Serializer};
132
133 impl Serialize for PolicyDecision {
134 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135 where
136 S: Serializer,
137 {
138 let mut len = 2;
140 if self.allow.is_some() {
141 len += 1;
142 }
143 if self.reason.is_some() {
144 len += 1;
145 }
146 let mut state = serializer.serialize_struct("PolicyDecision", len)?;
147 state.serialize_field("status", &self.status)?;
148 state.serialize_field("reasons", &self.reasons)?;
149 if let Some(allow) = &self.allow {
150 state.serialize_field("allow", allow)?;
151 }
152 if let Some(reason) = &self.reason {
153 state.serialize_field("reason", reason)?;
154 }
155 state.end()
156 }
157 }
158
159 impl<'de> Deserialize<'de> for PolicyDecision {
160 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161 where
162 D: Deserializer<'de>,
163 {
164 enum Field {
165 Allow,
166 Reason,
167 Status,
168 Reasons,
169 Unknown,
170 }
171
172 impl<'de> Deserialize<'de> for Field {
173 fn deserialize<D2>(deserializer: D2) -> Result<Self, D2::Error>
174 where
175 D2: Deserializer<'de>,
176 {
177 struct FieldVisitor;
178
179 impl<'de> Visitor<'de> for FieldVisitor {
180 type Value = Field;
181
182 fn expecting(
183 &self,
184 formatter: &mut core::fmt::Formatter,
185 ) -> core::fmt::Result {
186 formatter.write_str("`allow`, `reason`, `status`, or `reasons`")
187 }
188
189 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
190 where
191 E: de::Error,
192 {
193 Ok(match value {
194 "allow" => Field::Allow,
195 "reason" => Field::Reason,
196 "status" => Field::Status,
197 "reasons" => Field::Reasons,
198 _ => Field::Unknown,
199 })
200 }
201 }
202
203 deserializer.deserialize_identifier(FieldVisitor)
204 }
205 }
206
207 struct PolicyDecisionVisitor;
208
209 impl<'de> Visitor<'de> for PolicyDecisionVisitor {
210 type Value = PolicyDecision;
211
212 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
213 formatter.write_str("policy decision")
214 }
215
216 fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
217 where
218 M: MapAccess<'de>,
219 {
220 let mut allow: Option<Option<bool>> = None;
221 let mut reason: Option<Option<String>> = None;
222 let mut status: Option<PolicyDecisionStatus> = None;
223 let mut reasons: Option<Vec<String>> = None;
224
225 while let Some(key) = map.next_key()? {
226 match key {
227 Field::Allow => {
228 if allow.is_some() {
229 return Err(de::Error::duplicate_field("allow"));
230 }
231 allow = Some(map.next_value()?);
232 }
233 Field::Reason => {
234 if reason.is_some() {
235 return Err(de::Error::duplicate_field("reason"));
236 }
237 reason = Some(map.next_value()?);
238 }
239 Field::Status => {
240 if status.is_some() {
241 return Err(de::Error::duplicate_field("status"));
242 }
243 status = Some(map.next_value()?);
244 }
245 Field::Reasons => {
246 if reasons.is_some() {
247 return Err(de::Error::duplicate_field("reasons"));
248 }
249 reasons = Some(map.next_value()?);
250 }
251 Field::Unknown => {
252 let _ = map.next_value::<de::IgnoredAny>()?;
254 }
255 }
256 }
257
258 let status = status
259 .or_else(|| {
260 allow.flatten().map(|flag| {
261 if flag {
262 PolicyDecisionStatus::Allow
263 } else {
264 PolicyDecisionStatus::Deny
265 }
266 })
267 })
268 .unwrap_or(PolicyDecisionStatus::Allow);
269
270 let reasons_vec = match (reasons, reason.clone()) {
271 (Some(list), _) => list,
272 (None, Some(Some(msg))) => alloc::vec![msg],
273 (None, _) => Vec::new(),
274 };
275
276 Ok(PolicyDecision {
277 status,
278 reasons: reasons_vec,
279 allow: allow.flatten(),
280 reason: reason.flatten(),
281 })
282 }
283 }
284
285 deserializer.deserialize_struct(
286 "PolicyDecision",
287 &["status", "reasons", "allow", "reason"],
288 PolicyDecisionVisitor,
289 )
290 }
291 }
292}