unifly_api/command/requests/
policy.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::model::{EntityId, FirewallAction};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct CreateFirewallPolicyRequest {
8 pub name: String,
9 pub action: FirewallAction,
10 #[serde(alias = "source_zone")]
11 pub source_zone_id: EntityId,
12 #[serde(alias = "dest_zone")]
13 pub destination_zone_id: EntityId,
14 #[serde(default = "default_true")]
15 pub enabled: bool,
16 #[serde(default)]
17 pub logging_enabled: bool,
18 #[serde(default = "default_true")]
19 pub allow_return_traffic: bool,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub description: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub ip_version: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub connection_states: Option<Vec<String>>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub source_filter: Option<TrafficFilterSpec>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub destination_filter: Option<TrafficFilterSpec>,
30}
31
32fn default_true() -> bool {
33 true
34}
35
36#[derive(Debug, Clone, Default, Serialize, Deserialize)]
37pub struct UpdateFirewallPolicyRequest {
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub name: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub action: Option<FirewallAction>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub allow_return_traffic: Option<bool>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub enabled: Option<bool>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub description: Option<String>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub ip_version: Option<String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub connection_states: Option<Vec<String>>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub source_filter: Option<TrafficFilterSpec>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub destination_filter: Option<TrafficFilterSpec>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub logging_enabled: Option<bool>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(tag = "type", rename_all = "snake_case")]
62pub enum TrafficFilterSpec {
63 Network {
64 network_ids: Vec<String>,
65 #[serde(default)]
66 match_opposite: bool,
67 },
68 IpAddress {
69 addresses: Vec<String>,
70 #[serde(default)]
71 match_opposite: bool,
72 },
73 Port {
74 ports: Vec<String>,
75 #[serde(default)]
76 match_opposite: bool,
77 },
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct CreateFirewallZoneRequest {
82 pub name: String,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub description: Option<String>,
85 #[serde(alias = "networks")]
86 pub network_ids: Vec<EntityId>,
87}
88
89#[derive(Debug, Clone, Default, Serialize, Deserialize)]
90pub struct UpdateFirewallZoneRequest {
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub name: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub description: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none", alias = "networks")]
96 pub network_ids: Option<Vec<EntityId>>,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct CreateAclRuleRequest {
101 pub name: String,
102 #[serde(default = "default_acl_rule_type")]
103 pub rule_type: String,
104 pub action: FirewallAction,
105 #[serde(alias = "source_zone")]
106 pub source_zone_id: EntityId,
107 #[serde(alias = "dest_zone")]
108 pub destination_zone_id: EntityId,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub description: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub protocol: Option<String>,
113 #[serde(skip_serializing_if = "Option::is_none", alias = "src_port")]
114 pub source_port: Option<String>,
115 #[serde(skip_serializing_if = "Option::is_none", alias = "dst_port")]
116 pub destination_port: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub source_filter: Option<TrafficFilterSpec>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub destination_filter: Option<TrafficFilterSpec>,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub enforcing_device_filter: Option<Value>,
123 #[serde(default = "default_true")]
124 pub enabled: bool,
125}
126
127fn default_acl_rule_type() -> String {
128 "IP".into()
129}
130
131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
132pub struct UpdateAclRuleRequest {
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub name: Option<String>,
135 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
136 pub rule_type: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub action: Option<FirewallAction>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub enabled: Option<bool>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub description: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none", alias = "source_zone")]
144 pub source_zone_id: Option<EntityId>,
145 #[serde(skip_serializing_if = "Option::is_none", alias = "dest_zone")]
146 pub destination_zone_id: Option<EntityId>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub protocol: Option<String>,
149 #[serde(skip_serializing_if = "Option::is_none", alias = "src_port")]
150 pub source_port: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none", alias = "dst_port")]
152 pub destination_port: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub source_filter: Option<TrafficFilterSpec>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub destination_filter: Option<TrafficFilterSpec>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub enforcing_device_filter: Option<Value>,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct CreateNatPolicyRequest {
165 pub name: String,
166 #[serde(rename = "type", alias = "nat_type")]
168 pub nat_type: String,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub description: Option<String>,
171 #[serde(default = "default_true")]
172 pub enabled: bool,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub interface_id: Option<EntityId>,
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub protocol: Option<String>,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub src_address: Option<String>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub src_port: Option<String>,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub dst_address: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub dst_port: Option<String>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub translated_address: Option<String>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub translated_port: Option<String>,
190}
191
192#[derive(Debug, Clone, Default, Serialize, Deserialize)]
193pub struct UpdateNatPolicyRequest {
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub name: Option<String>,
196 #[serde(
198 rename = "type",
199 alias = "nat_type",
200 skip_serializing_if = "Option::is_none"
201 )]
202 pub nat_type: Option<String>,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub description: Option<String>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub enabled: Option<bool>,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub interface_id: Option<EntityId>,
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub protocol: Option<String>,
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub src_address: Option<String>,
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub src_port: Option<String>,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub dst_address: Option<String>,
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub dst_port: Option<String>,
220 #[serde(skip_serializing_if = "Option::is_none")]
221 pub translated_address: Option<String>,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub translated_port: Option<String>,
224}
225
226#[cfg(test)]
227mod tests {
228 use super::{CreateAclRuleRequest, UpdateAclRuleRequest};
229 use crate::model::FirewallAction;
230
231 #[test]
232 fn create_acl_rule_request_defaults_rule_type() {
233 let request: CreateAclRuleRequest = serde_json::from_value(serde_json::json!({
234 "name": "Allow IoT",
235 "action": "Allow",
236 "source_zone_id": "iot",
237 "destination_zone_id": "lan",
238 "enabled": true
239 }))
240 .expect("acl rule request should deserialize");
241
242 assert_eq!(request.rule_type, "IP");
243 }
244
245 #[test]
246 fn update_acl_rule_request_serializes_type_field() {
247 let request = UpdateAclRuleRequest {
248 rule_type: Some("DEVICE".into()),
249 action: Some(FirewallAction::Allow),
250 ..Default::default()
251 };
252
253 let value = serde_json::to_value(&request).expect("acl rule request should serialize");
254 assert_eq!(
255 value.get("type").and_then(serde_json::Value::as_str),
256 Some("DEVICE")
257 );
258 assert_eq!(value.get("rule_type"), None);
259 }
260}