1use std::collections::HashMap;
2
3use crate::types::*;
4
5#[derive(Debug, Default)]
7pub struct NetworkFirewallState {
8 pub firewalls: HashMap<String, Firewall>,
10 pub logging_configs: HashMap<String, serde_json::Value>,
12 pub rule_groups: HashMap<String, RuleGroup>,
14 pub firewall_policies: HashMap<String, FirewallPolicy>,
16 pub resource_policies: HashMap<String, ResourcePolicy>,
18 pub tls_inspection_configurations: HashMap<String, TlsInspectionConfiguration>,
20 pub vpc_endpoint_associations: HashMap<String, VpcEndpointAssociation>,
22 pub availability_zone_mappings: HashMap<String, Vec<AvailabilityZoneMapping>>,
24 pub transit_gateway_attachments: HashMap<String, TransitGatewayAttachment>,
26 pub proxies: HashMap<String, NfwProxy>,
28 pub proxy_configurations: HashMap<String, NfwProxyConfiguration>,
30 pub proxy_rule_groups: HashMap<String, NfwProxyRuleGroup>,
32 pub flow_operations: HashMap<String, FlowOperation>,
34 pub analysis_reports: HashMap<String, AnalysisReport>,
36 pub encryption_configs: HashMap<String, EncryptionConfig>,
38 pub analysis_settings: HashMap<String, Vec<String>>,
40}
41
42#[derive(Debug, thiserror::Error)]
44pub enum NfwError {
45 #[error("A resource with the specified name already exists: {name}")]
46 InvalidRequestDuplicateName { name: String },
47 #[error("Either FirewallArn or FirewallName must be specified")]
48 InvalidRequestFirewallIdentifier,
49 #[error("Either RuleGroupArn or RuleGroupName must be specified")]
50 InvalidRequestRuleGroupIdentifier,
51 #[error("Either FirewallPolicyArn or FirewallPolicyName must be specified")]
52 InvalidRequestFirewallPolicyIdentifier,
53 #[error(
54 "Either TLSInspectionConfigurationArn or TLSInspectionConfigurationName must be specified"
55 )]
56 InvalidRequestTlsInspectionConfigIdentifier,
57 #[error("Either ProxyArn or ProxyName must be specified")]
58 InvalidRequestProxyIdentifier,
59 #[error("Either ProxyConfigurationArn or ProxyConfigurationName must be specified")]
60 InvalidRequestProxyConfigIdentifier,
61 #[error("Either ProxyRuleGroupArn or ProxyRuleGroupName must be specified")]
62 InvalidRequestProxyRuleGroupIdentifier,
63 #[error("Resource '{identifier}' not found")]
64 ResourceNotFound { identifier: String },
65}
66
67impl NetworkFirewallState {
68 pub fn create_firewall(
69 &mut self,
70 firewall_name: &str,
71 firewall_policy_arn: &str,
72 vpc_id: &str,
73 subnet_mappings: Vec<SubnetMapping>,
74 description: Option<&str>,
75 delete_protection: bool,
76 tags: Vec<(String, String)>,
77 account_id: &str,
78 region: &str,
79 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
80 if self
81 .firewalls
82 .values()
83 .any(|f| f.firewall_name == firewall_name)
84 {
85 return Err(NfwError::InvalidRequestDuplicateName {
86 name: firewall_name.to_string(),
87 });
88 }
89
90 let firewall_id = uuid::Uuid::new_v4().to_string();
91 let arn =
92 format!("arn:aws:network-firewall:{region}:{account_id}:firewall/{firewall_name}");
93
94 let firewall = Firewall {
95 firewall_name: firewall_name.to_string(),
96 firewall_arn: arn.clone(),
97 firewall_id,
98 firewall_policy_arn: firewall_policy_arn.to_string(),
99 vpc_id: vpc_id.to_string(),
100 subnet_mappings,
101 delete_protection,
102 subnet_change_protection: false,
103 firewall_policy_change_protection: false,
104 availability_zone_change_protection: false,
105 description: description.map(|s| s.to_string()),
106 tags,
107 encryption_configuration: None,
108 };
109
110 self.firewalls.insert(arn.clone(), firewall);
111 let fw = self.firewalls.get(&arn).unwrap();
112 Ok((fw, FirewallStatus::default()))
113 }
114
115 pub fn describe_firewall(
116 &self,
117 firewall_name: Option<&str>,
118 firewall_arn: Option<&str>,
119 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
120 let fw = if let Some(arn) = firewall_arn {
121 self.firewalls.get(arn)
122 } else if let Some(name) = firewall_name {
123 self.firewalls.values().find(|f| f.firewall_name == name)
124 } else {
125 return Err(NfwError::InvalidRequestFirewallIdentifier);
126 };
127
128 match fw {
129 Some(f) => Ok((f, FirewallStatus::default())),
130 None => Err(resource_not_found_error(
131 firewall_arn.or(firewall_name).unwrap_or("unknown"),
132 )),
133 }
134 }
135
136 pub fn delete_firewall(
137 &mut self,
138 firewall_name: Option<&str>,
139 firewall_arn: Option<&str>,
140 ) -> Result<(Firewall, FirewallStatus), NfwError> {
141 let arn = if let Some(arn) = firewall_arn {
142 arn.to_string()
143 } else if let Some(name) = firewall_name {
144 match self.firewalls.values().find(|f| f.firewall_name == name) {
145 Some(f) => f.firewall_arn.clone(),
146 None => return Err(resource_not_found_error(name)),
147 }
148 } else {
149 return Err(NfwError::InvalidRequestFirewallIdentifier);
150 };
151
152 match self.firewalls.remove(&arn) {
153 Some(fw) => Ok((
154 fw,
155 FirewallStatus {
156 status: "DELETING".to_string(),
157 configuration_sync_state_summary: "IN_SYNC".to_string(),
158 },
159 )),
160 None => Err(resource_not_found_error(&arn)),
161 }
162 }
163
164 pub fn list_firewalls(&self) -> Vec<FirewallMetadata> {
165 self.firewalls
166 .values()
167 .map(|f| FirewallMetadata {
168 firewall_name: f.firewall_name.clone(),
169 firewall_arn: f.firewall_arn.clone(),
170 })
171 .collect()
172 }
173
174 #[allow(clippy::too_many_arguments)]
177 pub fn create_rule_group(
178 &mut self,
179 rule_group_name: &str,
180 rule_group_type: &str,
181 capacity: i32,
182 description: Option<&str>,
183 tags: Vec<(String, String)>,
184 rule_group_body: Option<serde_json::Value>,
185 rules: Option<&str>,
186 account_id: &str,
187 region: &str,
188 ) -> Result<&RuleGroup, NfwError> {
189 if self
190 .rule_groups
191 .values()
192 .any(|rg| rg.rule_group_name == rule_group_name)
193 {
194 return Err(NfwError::InvalidRequestDuplicateName {
195 name: rule_group_name.to_string(),
196 });
197 }
198
199 let rule_group_id = uuid::Uuid::new_v4().to_string();
200 let arn = format!(
201 "arn:aws:network-firewall:{region}:{account_id}:stateful-rulegroup/{rule_group_name}"
202 );
203
204 let rg = RuleGroup {
205 rule_group_name: rule_group_name.to_string(),
206 rule_group_arn: arn.clone(),
207 rule_group_id,
208 rule_group_type: rule_group_type.to_string(),
209 capacity,
210 description: description.map(|s| s.to_string()),
211 tags,
212 rule_group_body,
213 rules: rules.map(|s| s.to_string()),
214 encryption_configuration: None,
215 };
216
217 self.rule_groups.insert(arn.clone(), rg);
218 Ok(self.rule_groups.get(&arn).unwrap())
219 }
220
221 pub fn describe_rule_group(
222 &self,
223 rule_group_name: Option<&str>,
224 rule_group_arn: Option<&str>,
225 ) -> Result<&RuleGroup, NfwError> {
226 let rg = if let Some(arn) = rule_group_arn {
227 self.rule_groups.get(arn)
228 } else if let Some(name) = rule_group_name {
229 self.rule_groups
230 .values()
231 .find(|rg| rg.rule_group_name == name)
232 } else {
233 return Err(NfwError::InvalidRequestRuleGroupIdentifier);
234 };
235
236 match rg {
237 Some(rg) => Ok(rg),
238 None => Err(resource_not_found_error(
239 rule_group_arn.or(rule_group_name).unwrap_or("unknown"),
240 )),
241 }
242 }
243
244 pub fn delete_rule_group(
245 &mut self,
246 rule_group_name: Option<&str>,
247 rule_group_arn: Option<&str>,
248 ) -> Result<RuleGroup, NfwError> {
249 let arn = if let Some(arn) = rule_group_arn {
250 arn.to_string()
251 } else if let Some(name) = rule_group_name {
252 match self
253 .rule_groups
254 .values()
255 .find(|rg| rg.rule_group_name == name)
256 {
257 Some(rg) => rg.rule_group_arn.clone(),
258 None => return Err(resource_not_found_error(name)),
259 }
260 } else {
261 return Err(NfwError::InvalidRequestRuleGroupIdentifier);
262 };
263
264 match self.rule_groups.remove(&arn) {
265 Some(rg) => Ok(rg),
266 None => Err(resource_not_found_error(&arn)),
267 }
268 }
269
270 pub fn list_rule_groups(&self) -> Vec<RuleGroupMetadata> {
271 self.rule_groups
272 .values()
273 .map(|rg| RuleGroupMetadata {
274 name: rg.rule_group_name.clone(),
275 arn: rg.rule_group_arn.clone(),
276 })
277 .collect()
278 }
279
280 pub fn update_rule_group(
281 &mut self,
282 rule_group_name: Option<&str>,
283 rule_group_arn: Option<&str>,
284 description: Option<&str>,
285 rule_group_body: Option<serde_json::Value>,
286 rules: Option<&str>,
287 ) -> Result<&RuleGroup, NfwError> {
288 let arn = if let Some(arn) = rule_group_arn {
289 arn.to_string()
290 } else if let Some(name) = rule_group_name {
291 match self
292 .rule_groups
293 .values()
294 .find(|rg| rg.rule_group_name == name)
295 {
296 Some(rg) => rg.rule_group_arn.clone(),
297 None => return Err(resource_not_found_error(name)),
298 }
299 } else {
300 return Err(NfwError::InvalidRequestRuleGroupIdentifier);
301 };
302
303 let rg = match self.rule_groups.get_mut(&arn) {
304 Some(rg) => rg,
305 None => return Err(resource_not_found_error(&arn)),
306 };
307
308 if let Some(desc) = description {
309 rg.description = Some(desc.to_string());
310 }
311 if let Some(body) = rule_group_body {
312 rg.rule_group_body = Some(body);
313 }
314 if let Some(r) = rules {
315 rg.rules = Some(r.to_string());
316 }
317
318 Ok(self.rule_groups.get(&arn).unwrap())
319 }
320
321 pub fn create_firewall_policy(
324 &mut self,
325 firewall_policy_name: &str,
326 firewall_policy_body: serde_json::Value,
327 description: Option<&str>,
328 tags: Vec<(String, String)>,
329 account_id: &str,
330 region: &str,
331 ) -> Result<&FirewallPolicy, NfwError> {
332 if self
333 .firewall_policies
334 .values()
335 .any(|fp| fp.firewall_policy_name == firewall_policy_name)
336 {
337 return Err(NfwError::InvalidRequestDuplicateName {
338 name: firewall_policy_name.to_string(),
339 });
340 }
341
342 let firewall_policy_id = uuid::Uuid::new_v4().to_string();
343 let arn = format!(
344 "arn:aws:network-firewall:{region}:{account_id}:firewall-policy/{firewall_policy_name}"
345 );
346
347 let fp = FirewallPolicy {
348 firewall_policy_name: firewall_policy_name.to_string(),
349 firewall_policy_arn: arn.clone(),
350 firewall_policy_id,
351 description: description.map(|s| s.to_string()),
352 tags,
353 firewall_policy_body,
354 encryption_configuration: None,
355 };
356
357 self.firewall_policies.insert(arn.clone(), fp);
358 Ok(self.firewall_policies.get(&arn).unwrap())
359 }
360
361 pub fn describe_firewall_policy(
362 &self,
363 firewall_policy_name: Option<&str>,
364 firewall_policy_arn: Option<&str>,
365 ) -> Result<&FirewallPolicy, NfwError> {
366 let fp = if let Some(arn) = firewall_policy_arn {
367 self.firewall_policies.get(arn)
368 } else if let Some(name) = firewall_policy_name {
369 self.firewall_policies
370 .values()
371 .find(|fp| fp.firewall_policy_name == name)
372 } else {
373 return Err(NfwError::InvalidRequestFirewallPolicyIdentifier);
374 };
375
376 match fp {
377 Some(fp) => Ok(fp),
378 None => Err(resource_not_found_error(
379 firewall_policy_arn
380 .or(firewall_policy_name)
381 .unwrap_or("unknown"),
382 )),
383 }
384 }
385
386 pub fn delete_firewall_policy(
387 &mut self,
388 firewall_policy_name: Option<&str>,
389 firewall_policy_arn: Option<&str>,
390 ) -> Result<FirewallPolicy, NfwError> {
391 let arn = if let Some(arn) = firewall_policy_arn {
392 arn.to_string()
393 } else if let Some(name) = firewall_policy_name {
394 match self
395 .firewall_policies
396 .values()
397 .find(|fp| fp.firewall_policy_name == name)
398 {
399 Some(fp) => fp.firewall_policy_arn.clone(),
400 None => return Err(resource_not_found_error(name)),
401 }
402 } else {
403 return Err(NfwError::InvalidRequestFirewallPolicyIdentifier);
404 };
405
406 match self.firewall_policies.remove(&arn) {
407 Some(fp) => Ok(fp),
408 None => Err(resource_not_found_error(&arn)),
409 }
410 }
411
412 pub fn list_firewall_policies(&self) -> Vec<FirewallPolicyMetadata> {
413 self.firewall_policies
414 .values()
415 .map(|fp| FirewallPolicyMetadata {
416 name: fp.firewall_policy_name.clone(),
417 arn: fp.firewall_policy_arn.clone(),
418 })
419 .collect()
420 }
421
422 pub fn update_firewall_policy(
423 &mut self,
424 firewall_policy_name: Option<&str>,
425 firewall_policy_arn: Option<&str>,
426 firewall_policy_body: serde_json::Value,
427 description: Option<&str>,
428 ) -> Result<&FirewallPolicy, NfwError> {
429 let arn = if let Some(arn) = firewall_policy_arn {
430 arn.to_string()
431 } else if let Some(name) = firewall_policy_name {
432 match self
433 .firewall_policies
434 .values()
435 .find(|fp| fp.firewall_policy_name == name)
436 {
437 Some(fp) => fp.firewall_policy_arn.clone(),
438 None => return Err(resource_not_found_error(name)),
439 }
440 } else {
441 return Err(NfwError::InvalidRequestFirewallPolicyIdentifier);
442 };
443
444 let fp = match self.firewall_policies.get_mut(&arn) {
445 Some(fp) => fp,
446 None => return Err(resource_not_found_error(&arn)),
447 };
448
449 fp.firewall_policy_body = firewall_policy_body;
450 if let Some(desc) = description {
451 fp.description = Some(desc.to_string());
452 }
453
454 Ok(self.firewall_policies.get(&arn).unwrap())
455 }
456
457 pub fn list_tags_for_resource(
460 &self,
461 resource_arn: &str,
462 ) -> Result<Vec<(String, String)>, NfwError> {
463 if let Some(fw) = self.firewalls.get(resource_arn) {
464 return Ok(fw.tags.clone());
465 }
466 if let Some(rg) = self.rule_groups.get(resource_arn) {
467 return Ok(rg.tags.clone());
468 }
469 if let Some(fp) = self.firewall_policies.get(resource_arn) {
470 return Ok(fp.tags.clone());
471 }
472 Err(resource_not_found_error(resource_arn))
473 }
474
475 pub fn tag_resource(
476 &mut self,
477 resource_arn: &str,
478 tags: Vec<(String, String)>,
479 ) -> Result<(), NfwError> {
480 if let Some(fw) = self.firewalls.get_mut(resource_arn) {
481 for (k, v) in tags {
482 fw.tags.retain(|(ek, _)| *ek != k);
483 fw.tags.push((k, v));
484 }
485 return Ok(());
486 }
487 if let Some(rg) = self.rule_groups.get_mut(resource_arn) {
488 for (k, v) in tags {
489 rg.tags.retain(|(ek, _)| *ek != k);
490 rg.tags.push((k, v));
491 }
492 return Ok(());
493 }
494 if let Some(fp) = self.firewall_policies.get_mut(resource_arn) {
495 for (k, v) in tags {
496 fp.tags.retain(|(ek, _)| *ek != k);
497 fp.tags.push((k, v));
498 }
499 return Ok(());
500 }
501 Err(resource_not_found_error(resource_arn))
502 }
503
504 pub fn untag_resource(
505 &mut self,
506 resource_arn: &str,
507 tag_keys: Vec<&str>,
508 ) -> Result<(), NfwError> {
509 if let Some(fw) = self.firewalls.get_mut(resource_arn) {
510 fw.tags.retain(|(k, _)| !tag_keys.contains(&k.as_str()));
511 return Ok(());
512 }
513 if let Some(rg) = self.rule_groups.get_mut(resource_arn) {
514 rg.tags.retain(|(k, _)| !tag_keys.contains(&k.as_str()));
515 return Ok(());
516 }
517 if let Some(fp) = self.firewall_policies.get_mut(resource_arn) {
518 fp.tags.retain(|(k, _)| !tag_keys.contains(&k.as_str()));
519 return Ok(());
520 }
521 Err(resource_not_found_error(resource_arn))
522 }
523
524 pub fn put_resource_policy(
527 &mut self,
528 resource_arn: &str,
529 policy: &str,
530 ) -> Result<(), NfwError> {
531 self.resource_policies.insert(
532 resource_arn.to_string(),
533 ResourcePolicy {
534 resource_arn: resource_arn.to_string(),
535 policy: policy.to_string(),
536 },
537 );
538 Ok(())
539 }
540
541 pub fn describe_resource_policy(&self, resource_arn: &str) -> Result<&str, NfwError> {
542 match self.resource_policies.get(resource_arn) {
543 Some(rp) => Ok(&rp.policy),
544 None => Err(resource_not_found_error(resource_arn)),
545 }
546 }
547
548 pub fn delete_resource_policy(&mut self, resource_arn: &str) -> Result<(), NfwError> {
549 match self.resource_policies.remove(resource_arn) {
550 Some(_) => Ok(()),
551 None => Err(resource_not_found_error(resource_arn)),
552 }
553 }
554
555 pub fn update_firewall_description(
558 &mut self,
559 firewall_name: Option<&str>,
560 firewall_arn: Option<&str>,
561 description: Option<&str>,
562 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
563 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
564 let fw = self.firewalls.get_mut(&arn).unwrap();
565 fw.description = description.map(|s| s.to_string());
566 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
567 }
568
569 pub fn update_firewall_delete_protection(
570 &mut self,
571 firewall_name: Option<&str>,
572 firewall_arn: Option<&str>,
573 delete_protection: bool,
574 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
575 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
576 let fw = self.firewalls.get_mut(&arn).unwrap();
577 fw.delete_protection = delete_protection;
578 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
579 }
580
581 pub fn associate_firewall_policy(
582 &mut self,
583 firewall_name: Option<&str>,
584 firewall_arn: Option<&str>,
585 firewall_policy_arn: &str,
586 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
587 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
588 let fw = self.firewalls.get_mut(&arn).unwrap();
589 fw.firewall_policy_arn = firewall_policy_arn.to_string();
590 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
591 }
592
593 pub fn associate_subnets(
594 &mut self,
595 firewall_name: Option<&str>,
596 firewall_arn: Option<&str>,
597 subnet_mappings: Vec<SubnetMapping>,
598 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
599 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
600 let fw = self.firewalls.get_mut(&arn).unwrap();
601 for sm in subnet_mappings {
602 if !fw
603 .subnet_mappings
604 .iter()
605 .any(|e| e.subnet_id == sm.subnet_id)
606 {
607 fw.subnet_mappings.push(sm);
608 }
609 }
610 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
611 }
612
613 pub fn disassociate_subnets(
614 &mut self,
615 firewall_name: Option<&str>,
616 firewall_arn: Option<&str>,
617 subnet_ids: Vec<&str>,
618 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
619 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
620 let fw = self.firewalls.get_mut(&arn).unwrap();
621 fw.subnet_mappings
622 .retain(|sm| !subnet_ids.contains(&sm.subnet_id.as_str()));
623 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
624 }
625
626 pub fn update_subnet_change_protection(
627 &mut self,
628 firewall_name: Option<&str>,
629 firewall_arn: Option<&str>,
630 subnet_change_protection: bool,
631 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
632 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
633 let fw = self.firewalls.get_mut(&arn).unwrap();
634 fw.subnet_change_protection = subnet_change_protection;
635 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
636 }
637
638 pub fn update_firewall_policy_change_protection(
639 &mut self,
640 firewall_name: Option<&str>,
641 firewall_arn: Option<&str>,
642 firewall_policy_change_protection: bool,
643 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
644 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
645 let fw = self.firewalls.get_mut(&arn).unwrap();
646 fw.firewall_policy_change_protection = firewall_policy_change_protection;
647 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
648 }
649
650 pub fn update_availability_zone_change_protection(
651 &mut self,
652 firewall_name: Option<&str>,
653 firewall_arn: Option<&str>,
654 availability_zone_change_protection: bool,
655 ) -> Result<(&Firewall, FirewallStatus), NfwError> {
656 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
657 let fw = self.firewalls.get_mut(&arn).unwrap();
658 fw.availability_zone_change_protection = availability_zone_change_protection;
659 Ok((self.firewalls.get(&arn).unwrap(), FirewallStatus::default()))
660 }
661
662 pub fn create_tls_inspection_configuration(
665 &mut self,
666 name: &str,
667 body: serde_json::Value,
668 description: Option<&str>,
669 tags: Vec<(String, String)>,
670 account_id: &str,
671 region: &str,
672 ) -> Result<&TlsInspectionConfiguration, NfwError> {
673 if self
674 .tls_inspection_configurations
675 .values()
676 .any(|t| t.name == name)
677 {
678 return Err(NfwError::InvalidRequestDuplicateName {
679 name: name.to_string(),
680 });
681 }
682
683 let id = uuid::Uuid::new_v4().to_string();
684 let arn =
685 format!("arn:aws:network-firewall:{region}:{account_id}:tls-configuration/{name}");
686
687 let tls = TlsInspectionConfiguration {
688 name: name.to_string(),
689 arn: arn.clone(),
690 id,
691 description: description.map(|s| s.to_string()),
692 tags,
693 body,
694 };
695
696 self.tls_inspection_configurations.insert(arn.clone(), tls);
697 Ok(self.tls_inspection_configurations.get(&arn).unwrap())
698 }
699
700 pub fn describe_tls_inspection_configuration(
701 &self,
702 name: Option<&str>,
703 arn: Option<&str>,
704 ) -> Result<&TlsInspectionConfiguration, NfwError> {
705 let tls = if let Some(a) = arn {
706 self.tls_inspection_configurations.get(a)
707 } else if let Some(n) = name {
708 self.tls_inspection_configurations
709 .values()
710 .find(|t| t.name == n)
711 } else {
712 return Err(NfwError::InvalidRequestTlsInspectionConfigIdentifier);
713 };
714
715 match tls {
716 Some(t) => Ok(t),
717 None => Err(resource_not_found_error(arn.or(name).unwrap_or("unknown"))),
718 }
719 }
720
721 pub fn delete_tls_inspection_configuration(
722 &mut self,
723 name: Option<&str>,
724 arn: Option<&str>,
725 ) -> Result<TlsInspectionConfiguration, NfwError> {
726 let resolved_arn = if let Some(a) = arn {
727 a.to_string()
728 } else if let Some(n) = name {
729 match self
730 .tls_inspection_configurations
731 .values()
732 .find(|t| t.name == n)
733 {
734 Some(t) => t.arn.clone(),
735 None => return Err(resource_not_found_error(n)),
736 }
737 } else {
738 return Err(NfwError::InvalidRequestTlsInspectionConfigIdentifier);
739 };
740
741 match self.tls_inspection_configurations.remove(&resolved_arn) {
742 Some(t) => Ok(t),
743 None => Err(resource_not_found_error(&resolved_arn)),
744 }
745 }
746
747 pub fn update_tls_inspection_configuration(
748 &mut self,
749 name: Option<&str>,
750 arn: Option<&str>,
751 body: serde_json::Value,
752 description: Option<&str>,
753 ) -> Result<&TlsInspectionConfiguration, NfwError> {
754 let resolved_arn = if let Some(a) = arn {
755 a.to_string()
756 } else if let Some(n) = name {
757 match self
758 .tls_inspection_configurations
759 .values()
760 .find(|t| t.name == n)
761 {
762 Some(t) => t.arn.clone(),
763 None => return Err(resource_not_found_error(n)),
764 }
765 } else {
766 return Err(NfwError::InvalidRequestTlsInspectionConfigIdentifier);
767 };
768
769 let tls = match self.tls_inspection_configurations.get_mut(&resolved_arn) {
770 Some(t) => t,
771 None => return Err(resource_not_found_error(&resolved_arn)),
772 };
773
774 tls.body = body;
775 if let Some(desc) = description {
776 tls.description = Some(desc.to_string());
777 }
778
779 Ok(self
780 .tls_inspection_configurations
781 .get(&resolved_arn)
782 .unwrap())
783 }
784
785 pub fn list_tls_inspection_configurations(&self) -> Vec<TlsInspectionConfigurationMetadata> {
786 self.tls_inspection_configurations
787 .values()
788 .map(|t| TlsInspectionConfigurationMetadata {
789 name: t.name.clone(),
790 arn: t.arn.clone(),
791 })
792 .collect()
793 }
794
795 pub fn create_vpc_endpoint_association(
798 &mut self,
799 firewall_arn: &str,
800 vpc_id: &str,
801 subnet_id: &str,
802 description: Option<&str>,
803 tags: Vec<(String, String)>,
804 account_id: &str,
805 region: &str,
806 ) -> Result<&VpcEndpointAssociation, NfwError> {
807 let id = uuid::Uuid::new_v4().to_string();
808 let arn =
809 format!("arn:aws:network-firewall:{region}:{account_id}:vpc-endpoint-association/{id}");
810
811 let assoc = VpcEndpointAssociation {
812 vpc_endpoint_association_arn: arn.clone(),
813 vpc_endpoint_association_id: id,
814 firewall_arn: firewall_arn.to_string(),
815 vpc_id: vpc_id.to_string(),
816 subnet_id: subnet_id.to_string(),
817 description: description.map(|s| s.to_string()),
818 tags,
819 };
820
821 self.vpc_endpoint_associations.insert(arn.clone(), assoc);
822 Ok(self.vpc_endpoint_associations.get(&arn).unwrap())
823 }
824
825 pub fn describe_vpc_endpoint_association(
826 &self,
827 arn: &str,
828 ) -> Result<&VpcEndpointAssociation, NfwError> {
829 match self.vpc_endpoint_associations.get(arn) {
830 Some(a) => Ok(a),
831 None => Err(resource_not_found_error(arn)),
832 }
833 }
834
835 pub fn delete_vpc_endpoint_association(
836 &mut self,
837 arn: &str,
838 ) -> Result<VpcEndpointAssociation, NfwError> {
839 match self.vpc_endpoint_associations.remove(arn) {
840 Some(a) => Ok(a),
841 None => Err(resource_not_found_error(arn)),
842 }
843 }
844
845 pub fn list_vpc_endpoint_associations(
846 &self,
847 firewall_arn: Option<&str>,
848 ) -> Vec<&VpcEndpointAssociation> {
849 self.vpc_endpoint_associations
850 .values()
851 .filter(|a| {
852 if let Some(fw_arn) = firewall_arn {
853 a.firewall_arn == fw_arn
854 } else {
855 true
856 }
857 })
858 .collect()
859 }
860
861 pub fn associate_availability_zones(
864 &mut self,
865 firewall_name: Option<&str>,
866 firewall_arn: Option<&str>,
867 zones: Vec<AvailabilityZoneMapping>,
868 ) -> Result<(&Firewall, Vec<AvailabilityZoneMapping>), NfwError> {
869 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
870 let mappings = self
871 .availability_zone_mappings
872 .entry(arn.clone())
873 .or_default();
874 for zone in zones {
875 if !mappings
876 .iter()
877 .any(|m| m.availability_zone == zone.availability_zone)
878 {
879 mappings.push(zone);
880 }
881 }
882 let result_mappings = self.availability_zone_mappings.get(&arn).unwrap().clone();
883 Ok((self.firewalls.get(&arn).unwrap(), result_mappings))
884 }
885
886 pub fn disassociate_availability_zones(
887 &mut self,
888 firewall_name: Option<&str>,
889 firewall_arn: Option<&str>,
890 zones: Vec<&str>,
891 ) -> Result<(&Firewall, Vec<AvailabilityZoneMapping>), NfwError> {
892 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
893 let mappings = self
894 .availability_zone_mappings
895 .entry(arn.clone())
896 .or_default();
897 mappings.retain(|m| !zones.contains(&m.availability_zone.as_str()));
898 let result_mappings = self.availability_zone_mappings.get(&arn).unwrap().clone();
899 Ok((self.firewalls.get(&arn).unwrap(), result_mappings))
900 }
901
902 pub fn accept_transit_gateway_attachment(
905 &mut self,
906 transit_gateway_attachment_id: &str,
907 ) -> Result<&TransitGatewayAttachment, NfwError> {
908 let attachment = self
909 .transit_gateway_attachments
910 .get_mut(transit_gateway_attachment_id);
911 match attachment {
912 Some(a) => {
913 a.status = "ACCEPTED".to_string();
914 Ok(self
915 .transit_gateway_attachments
916 .get(transit_gateway_attachment_id)
917 .unwrap())
918 }
919 None => {
920 let att = TransitGatewayAttachment {
922 transit_gateway_attachment_id: transit_gateway_attachment_id.to_string(),
923 status: "ACCEPTED".to_string(),
924 };
925 self.transit_gateway_attachments
926 .insert(transit_gateway_attachment_id.to_string(), att);
927 Ok(self
928 .transit_gateway_attachments
929 .get(transit_gateway_attachment_id)
930 .unwrap())
931 }
932 }
933 }
934
935 pub fn reject_transit_gateway_attachment(
936 &mut self,
937 transit_gateway_attachment_id: &str,
938 ) -> Result<&TransitGatewayAttachment, NfwError> {
939 let attachment = self
940 .transit_gateway_attachments
941 .get_mut(transit_gateway_attachment_id);
942 match attachment {
943 Some(a) => {
944 a.status = "REJECTED".to_string();
945 Ok(self
946 .transit_gateway_attachments
947 .get(transit_gateway_attachment_id)
948 .unwrap())
949 }
950 None => {
951 let att = TransitGatewayAttachment {
952 transit_gateway_attachment_id: transit_gateway_attachment_id.to_string(),
953 status: "REJECTED".to_string(),
954 };
955 self.transit_gateway_attachments
956 .insert(transit_gateway_attachment_id.to_string(), att);
957 Ok(self
958 .transit_gateway_attachments
959 .get(transit_gateway_attachment_id)
960 .unwrap())
961 }
962 }
963 }
964
965 pub fn delete_transit_gateway_attachment(
966 &mut self,
967 transit_gateway_attachment_id: &str,
968 ) -> Result<TransitGatewayAttachment, NfwError> {
969 match self
970 .transit_gateway_attachments
971 .remove(transit_gateway_attachment_id)
972 {
973 Some(mut a) => {
974 a.status = "DELETING".to_string();
975 Ok(a)
976 }
977 None => Err(resource_not_found_error(transit_gateway_attachment_id)),
978 }
979 }
980
981 pub fn create_proxy(
984 &mut self,
985 proxy_name: &str,
986 nat_gateway_id: &str,
987 proxy_configuration_arn: Option<&str>,
988 proxy_configuration_name: Option<&str>,
989 tags: Vec<(String, String)>,
990 body: serde_json::Value,
991 account_id: &str,
992 region: &str,
993 ) -> Result<&NfwProxy, NfwError> {
994 if self.proxies.values().any(|p| p.proxy_name == proxy_name) {
995 return Err(NfwError::InvalidRequestDuplicateName {
996 name: proxy_name.to_string(),
997 });
998 }
999 let arn = format!("arn:aws:network-firewall:{region}:{account_id}:proxy/{proxy_name}");
1000 let proxy = NfwProxy {
1001 proxy_name: proxy_name.to_string(),
1002 proxy_arn: arn.clone(),
1003 nat_gateway_id: nat_gateway_id.to_string(),
1004 proxy_configuration_arn: proxy_configuration_arn.map(|s| s.to_string()),
1005 proxy_configuration_name: proxy_configuration_name.map(|s| s.to_string()),
1006 proxy_state: "READY".to_string(),
1007 tags,
1008 body,
1009 };
1010 self.proxies.insert(arn.clone(), proxy);
1011 Ok(self.proxies.get(&arn).unwrap())
1012 }
1013
1014 pub fn describe_proxy(
1015 &self,
1016 proxy_name: Option<&str>,
1017 proxy_arn: Option<&str>,
1018 ) -> Result<&NfwProxy, NfwError> {
1019 let proxy = if let Some(arn) = proxy_arn {
1020 self.proxies.get(arn)
1021 } else if let Some(name) = proxy_name {
1022 self.proxies.values().find(|p| p.proxy_name == name)
1023 } else {
1024 return Err(NfwError::InvalidRequestProxyIdentifier);
1025 };
1026 match proxy {
1027 Some(p) => Ok(p),
1028 None => Err(resource_not_found_error(
1029 proxy_arn.or(proxy_name).unwrap_or("unknown"),
1030 )),
1031 }
1032 }
1033
1034 pub fn delete_proxy(
1035 &mut self,
1036 proxy_name: Option<&str>,
1037 proxy_arn: Option<&str>,
1038 ) -> Result<NfwProxy, NfwError> {
1039 let arn = if let Some(arn) = proxy_arn {
1040 arn.to_string()
1041 } else if let Some(name) = proxy_name {
1042 match self.proxies.values().find(|p| p.proxy_name == name) {
1043 Some(p) => p.proxy_arn.clone(),
1044 None => return Err(resource_not_found_error(name)),
1045 }
1046 } else {
1047 return Err(NfwError::InvalidRequestProxyIdentifier);
1048 };
1049 match self.proxies.remove(&arn) {
1050 Some(p) => Ok(p),
1051 None => Err(resource_not_found_error(&arn)),
1052 }
1053 }
1054
1055 pub fn list_proxies(&self) -> Vec<&NfwProxy> {
1056 self.proxies.values().collect()
1057 }
1058
1059 pub fn update_proxy(
1060 &mut self,
1061 proxy_name: Option<&str>,
1062 proxy_arn: Option<&str>,
1063 body: serde_json::Value,
1064 ) -> Result<&NfwProxy, NfwError> {
1065 let arn = if let Some(arn) = proxy_arn {
1066 arn.to_string()
1067 } else if let Some(name) = proxy_name {
1068 match self.proxies.values().find(|p| p.proxy_name == name) {
1069 Some(p) => p.proxy_arn.clone(),
1070 None => return Err(resource_not_found_error(name)),
1071 }
1072 } else {
1073 return Err(NfwError::InvalidRequestProxyIdentifier);
1074 };
1075 let proxy = match self.proxies.get_mut(&arn) {
1076 Some(p) => p,
1077 None => return Err(resource_not_found_error(&arn)),
1078 };
1079 proxy.body = body;
1080 Ok(self.proxies.get(&arn).unwrap())
1081 }
1082
1083 pub fn create_proxy_configuration(
1086 &mut self,
1087 name: &str,
1088 description: Option<&str>,
1089 tags: Vec<(String, String)>,
1090 body: serde_json::Value,
1091 account_id: &str,
1092 region: &str,
1093 ) -> Result<&NfwProxyConfiguration, NfwError> {
1094 if self
1095 .proxy_configurations
1096 .values()
1097 .any(|c| c.proxy_configuration_name == name)
1098 {
1099 return Err(NfwError::InvalidRequestDuplicateName {
1100 name: name.to_string(),
1101 });
1102 }
1103 let arn =
1104 format!("arn:aws:network-firewall:{region}:{account_id}:proxy-configuration/{name}");
1105 let config = NfwProxyConfiguration {
1106 proxy_configuration_name: name.to_string(),
1107 proxy_configuration_arn: arn.clone(),
1108 description: description.map(|s| s.to_string()),
1109 tags,
1110 body,
1111 };
1112 self.proxy_configurations.insert(arn.clone(), config);
1113 Ok(self.proxy_configurations.get(&arn).unwrap())
1114 }
1115
1116 pub fn describe_proxy_configuration(
1117 &self,
1118 name: Option<&str>,
1119 arn: Option<&str>,
1120 ) -> Result<&NfwProxyConfiguration, NfwError> {
1121 let config = if let Some(a) = arn {
1122 self.proxy_configurations.get(a)
1123 } else if let Some(n) = name {
1124 self.proxy_configurations
1125 .values()
1126 .find(|c| c.proxy_configuration_name == n)
1127 } else {
1128 return Err(NfwError::InvalidRequestProxyConfigIdentifier);
1129 };
1130 match config {
1131 Some(c) => Ok(c),
1132 None => Err(resource_not_found_error(arn.or(name).unwrap_or("unknown"))),
1133 }
1134 }
1135
1136 pub fn delete_proxy_configuration(
1137 &mut self,
1138 name: Option<&str>,
1139 arn: Option<&str>,
1140 ) -> Result<NfwProxyConfiguration, NfwError> {
1141 let resolved_arn = if let Some(a) = arn {
1142 a.to_string()
1143 } else if let Some(n) = name {
1144 match self
1145 .proxy_configurations
1146 .values()
1147 .find(|c| c.proxy_configuration_name == n)
1148 {
1149 Some(c) => c.proxy_configuration_arn.clone(),
1150 None => return Err(resource_not_found_error(n)),
1151 }
1152 } else {
1153 return Err(NfwError::InvalidRequestProxyConfigIdentifier);
1154 };
1155 match self.proxy_configurations.remove(&resolved_arn) {
1156 Some(c) => Ok(c),
1157 None => Err(resource_not_found_error(&resolved_arn)),
1158 }
1159 }
1160
1161 pub fn list_proxy_configurations(&self) -> Vec<&NfwProxyConfiguration> {
1162 self.proxy_configurations.values().collect()
1163 }
1164
1165 pub fn update_proxy_configuration(
1166 &mut self,
1167 name: Option<&str>,
1168 arn: Option<&str>,
1169 body: serde_json::Value,
1170 ) -> Result<&NfwProxyConfiguration, NfwError> {
1171 let resolved_arn = if let Some(a) = arn {
1172 a.to_string()
1173 } else if let Some(n) = name {
1174 match self
1175 .proxy_configurations
1176 .values()
1177 .find(|c| c.proxy_configuration_name == n)
1178 {
1179 Some(c) => c.proxy_configuration_arn.clone(),
1180 None => return Err(resource_not_found_error(n)),
1181 }
1182 } else {
1183 return Err(NfwError::InvalidRequestProxyConfigIdentifier);
1184 };
1185 let config = match self.proxy_configurations.get_mut(&resolved_arn) {
1186 Some(c) => c,
1187 None => return Err(resource_not_found_error(&resolved_arn)),
1188 };
1189 config.body = body;
1190 Ok(self.proxy_configurations.get(&resolved_arn).unwrap())
1191 }
1192
1193 pub fn create_proxy_rule_group(
1196 &mut self,
1197 name: &str,
1198 description: Option<&str>,
1199 tags: Vec<(String, String)>,
1200 body: serde_json::Value,
1201 account_id: &str,
1202 region: &str,
1203 ) -> Result<&NfwProxyRuleGroup, NfwError> {
1204 if self
1205 .proxy_rule_groups
1206 .values()
1207 .any(|g| g.proxy_rule_group_name == name)
1208 {
1209 return Err(NfwError::InvalidRequestDuplicateName {
1210 name: name.to_string(),
1211 });
1212 }
1213 let arn = format!("arn:aws:network-firewall:{region}:{account_id}:proxy-rule-group/{name}");
1214 let group = NfwProxyRuleGroup {
1215 proxy_rule_group_name: name.to_string(),
1216 proxy_rule_group_arn: arn.clone(),
1217 description: description.map(|s| s.to_string()),
1218 tags,
1219 body,
1220 };
1221 self.proxy_rule_groups.insert(arn.clone(), group);
1222 Ok(self.proxy_rule_groups.get(&arn).unwrap())
1223 }
1224
1225 pub fn describe_proxy_rule_group(
1226 &self,
1227 name: Option<&str>,
1228 arn: Option<&str>,
1229 ) -> Result<&NfwProxyRuleGroup, NfwError> {
1230 let group = if let Some(a) = arn {
1231 self.proxy_rule_groups.get(a)
1232 } else if let Some(n) = name {
1233 self.proxy_rule_groups
1234 .values()
1235 .find(|g| g.proxy_rule_group_name == n)
1236 } else {
1237 return Err(NfwError::InvalidRequestProxyRuleGroupIdentifier);
1238 };
1239 match group {
1240 Some(g) => Ok(g),
1241 None => Err(resource_not_found_error(arn.or(name).unwrap_or("unknown"))),
1242 }
1243 }
1244
1245 pub fn delete_proxy_rule_group(
1246 &mut self,
1247 name: Option<&str>,
1248 arn: Option<&str>,
1249 ) -> Result<NfwProxyRuleGroup, NfwError> {
1250 let resolved_arn = if let Some(a) = arn {
1251 a.to_string()
1252 } else if let Some(n) = name {
1253 match self
1254 .proxy_rule_groups
1255 .values()
1256 .find(|g| g.proxy_rule_group_name == n)
1257 {
1258 Some(g) => g.proxy_rule_group_arn.clone(),
1259 None => return Err(resource_not_found_error(n)),
1260 }
1261 } else {
1262 return Err(NfwError::InvalidRequestProxyRuleGroupIdentifier);
1263 };
1264 match self.proxy_rule_groups.remove(&resolved_arn) {
1265 Some(g) => Ok(g),
1266 None => Err(resource_not_found_error(&resolved_arn)),
1267 }
1268 }
1269
1270 pub fn list_proxy_rule_groups(&self) -> Vec<&NfwProxyRuleGroup> {
1271 self.proxy_rule_groups.values().collect()
1272 }
1273
1274 pub fn create_flow_operation(
1277 &mut self,
1278 firewall_arn: &str,
1279 flow_operation_type: &str,
1280 body: serde_json::Value,
1281 ) -> &FlowOperation {
1282 let id = uuid::Uuid::new_v4().to_string();
1283 let op = FlowOperation {
1284 flow_operation_id: id.clone(),
1285 firewall_arn: firewall_arn.to_string(),
1286 flow_operation_type: flow_operation_type.to_string(),
1287 flow_operation_status: "COMPLETED".to_string(),
1288 body,
1289 };
1290 self.flow_operations.insert(id.clone(), op);
1291 self.flow_operations.get(&id).unwrap()
1292 }
1293
1294 pub fn describe_flow_operation(
1295 &self,
1296 flow_operation_id: &str,
1297 ) -> Result<&FlowOperation, NfwError> {
1298 match self.flow_operations.get(flow_operation_id) {
1299 Some(op) => Ok(op),
1300 None => Err(resource_not_found_error(flow_operation_id)),
1301 }
1302 }
1303
1304 pub fn list_flow_operations(&self, firewall_arn: &str) -> Vec<&FlowOperation> {
1305 self.flow_operations
1306 .values()
1307 .filter(|op| op.firewall_arn == firewall_arn)
1308 .collect()
1309 }
1310
1311 pub fn start_analysis_report(
1314 &mut self,
1315 firewall_arn: &str,
1316 analysis_type: &str,
1317 ) -> &AnalysisReport {
1318 let id = uuid::Uuid::new_v4().to_string();
1319 let report = AnalysisReport {
1320 analysis_report_id: id.clone(),
1321 firewall_arn: firewall_arn.to_string(),
1322 analysis_type: analysis_type.to_string(),
1323 status: "COMPLETED".to_string(),
1324 };
1325 self.analysis_reports.insert(id.clone(), report);
1326 self.analysis_reports.get(&id).unwrap()
1327 }
1328
1329 pub fn list_analysis_reports(&self, firewall_arn: &str) -> Vec<&AnalysisReport> {
1330 self.analysis_reports
1331 .values()
1332 .filter(|r| r.firewall_arn == firewall_arn)
1333 .collect()
1334 }
1335
1336 pub fn get_analysis_report(
1337 &self,
1338 analysis_report_id: &str,
1339 ) -> Result<&AnalysisReport, NfwError> {
1340 match self.analysis_reports.get(analysis_report_id) {
1341 Some(r) => Ok(r),
1342 None => Err(resource_not_found_error(analysis_report_id)),
1343 }
1344 }
1345
1346 pub fn update_analysis_settings(
1349 &mut self,
1350 firewall_name: Option<&str>,
1351 firewall_arn: Option<&str>,
1352 enabled_analysis_types: Vec<String>,
1353 ) -> Result<(&Firewall, Vec<String>), NfwError> {
1354 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
1355 self.analysis_settings
1356 .insert(arn.clone(), enabled_analysis_types);
1357 let types = self.analysis_settings.get(&arn).unwrap().clone();
1358 Ok((self.firewalls.get(&arn).unwrap(), types))
1359 }
1360
1361 pub fn update_encryption_configuration(
1364 &mut self,
1365 firewall_name: Option<&str>,
1366 firewall_arn: Option<&str>,
1367 config: EncryptionConfig,
1368 ) -> Result<(&Firewall, EncryptionConfig), NfwError> {
1369 let arn = self.resolve_firewall_arn(firewall_name, firewall_arn)?;
1370 self.encryption_configs.insert(arn.clone(), config);
1371 let enc = self.encryption_configs.get(&arn).unwrap().clone();
1372 Ok((self.firewalls.get(&arn).unwrap(), enc))
1373 }
1374
1375 fn resolve_firewall_arn(
1376 &self,
1377 firewall_name: Option<&str>,
1378 firewall_arn: Option<&str>,
1379 ) -> Result<String, NfwError> {
1380 if let Some(arn) = firewall_arn {
1381 if self.firewalls.contains_key(arn) {
1382 return Ok(arn.to_string());
1383 }
1384 return Err(resource_not_found_error(arn));
1385 }
1386 if let Some(name) = firewall_name {
1387 return match self.firewalls.values().find(|f| f.firewall_name == name) {
1388 Some(f) => Ok(f.firewall_arn.clone()),
1389 None => Err(resource_not_found_error(name)),
1390 };
1391 }
1392 Err(NfwError::InvalidRequestFirewallIdentifier)
1393 }
1394}
1395
1396fn resource_not_found_error(identifier: &str) -> NfwError {
1397 NfwError::ResourceNotFound {
1398 identifier: identifier.to_string(),
1399 }
1400}