wick_config/
audit.rs

1use std::path::PathBuf;
2use std::str::FromStr;
3
4use url::Url;
5
6use crate::config::{
7  Binding,
8  ConfigOrDefinition,
9  ConfigurationTreeNode,
10  ResourceDefinition,
11  TcpPort,
12  UdpPort,
13  UrlResource,
14  Volume,
15};
16use crate::WickConfiguration;
17
18/// An audit report for a component or application.
19#[derive(Debug, Clone, serde::Serialize)]
20#[non_exhaustive]
21pub struct Audit {
22  /// The name of the audited element.
23  pub name: String,
24  /// The resources used by the audited element.
25  #[serde(skip_serializing_if = "Vec::is_empty")]
26  pub resources: Vec<AuditedResourceBinding>,
27  /// The components the audited element imports.
28  #[serde(skip_serializing_if = "Vec::is_empty")]
29  pub imports: Vec<Audit>,
30}
31
32impl Audit {
33  /// Audit a configuration tree.
34  pub fn new(tree: &ConfigurationTreeNode<WickConfiguration>) -> Self {
35    Self {
36      name: tree.name.clone(),
37      resources: tree
38        .element
39        .resources()
40        .iter()
41        .map(AuditedResourceBinding::from)
42        .collect::<Vec<_>>(),
43      imports: tree.children.iter().map(Self::config_or_def).collect::<Vec<_>>(),
44    }
45  }
46
47  /// Audit a flattened list of configuration elements.
48  pub fn new_flattened(elements: &[ConfigOrDefinition<WickConfiguration>]) -> Vec<Audit> {
49    elements.iter().map(Self::config_or_def).collect::<Vec<_>>()
50  }
51
52  pub(crate) fn config_or_def(el: &ConfigOrDefinition<WickConfiguration>) -> Self {
53    match el {
54      crate::config::ConfigOrDefinition::Config(c) => Audit::new(c),
55      crate::config::ConfigOrDefinition::Definition { id, .. } => Audit {
56        name: id.clone(),
57        resources: Vec::new(),
58        imports: Vec::new(),
59      },
60    }
61  }
62}
63
64impl From<&ResourceDefinition> for AuditedResource {
65  fn from(value: &ResourceDefinition) -> Self {
66    match value {
67      ResourceDefinition::TcpPort(v) => Self::TcpPort(AuditedPort {
68        port: *v.port.value_unchecked(),
69        address: v.host.value_unchecked().clone(),
70      }),
71      ResourceDefinition::UdpPort(v) => Self::UdpPort(AuditedPort {
72        port: *v.port.value_unchecked(),
73        address: v.host.value_unchecked().clone(),
74      }),
75      ResourceDefinition::Url(v) => Self::Url(AuditedUrl::from(v.url.value_unchecked().clone())),
76      ResourceDefinition::Volume(v) => Self::Volume(AuditedVolume {
77        path: v.path().unwrap(),
78      }),
79    }
80  }
81}
82
83/// A rendeder resource binding.
84#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
85pub struct AuditedResourceBinding {
86  pub(crate) name: String,
87  pub(crate) resource: AuditedResource,
88}
89
90impl From<AuditedResourceBinding> for ResourceDefinition {
91  fn from(value: AuditedResourceBinding) -> Self {
92    match value.resource {
93      AuditedResource::TcpPort(v) => Self::TcpPort(TcpPort::new(v.address, v.port)),
94      AuditedResource::UdpPort(v) => Self::UdpPort(UdpPort::new(v.address, v.port)),
95      AuditedResource::Url(v) => Self::Url(UrlResource::new(v.url)),
96      AuditedResource::Volume(v) => Self::Volume(Volume::new(v.path.to_string_lossy().to_string())),
97    }
98  }
99}
100
101impl std::fmt::Display for AuditedResourceBinding {
102  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103    write!(f, "{}: {}", self.name, self.resource)
104  }
105}
106
107impl From<&Binding<ResourceDefinition>> for AuditedResourceBinding {
108  fn from(value: &Binding<ResourceDefinition>) -> Self {
109    Self {
110      name: value.id().to_owned(),
111      resource: AuditedResource::from(&value.kind),
112    }
113  }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
117#[serde(tag = "kind")]
118
119/// The possible types of resources. Resources are system-level resources and sensitive configuration.
120pub enum AuditedResource {
121  /// A variant representing a [crate::config::TcpPort] type.
122  #[serde(rename = "wick/resource/tcpport@v1")]
123  TcpPort(AuditedPort),
124  /// A variant representing a [crate::config::UdpPort] type.
125  #[serde(rename = "wick/resource/udpport@v1")]
126  UdpPort(AuditedPort),
127  /// A variant representing a [crate::config::UrlResource] type.
128  #[serde(rename = "wick/resource/url@v1")]
129  Url(AuditedUrl),
130  /// A variant representing a [crate::config::Volume] type.
131  #[serde(rename = "wick/resource/volume@v1")]
132  Volume(AuditedVolume),
133}
134
135impl std::fmt::Display for AuditedResource {
136  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137    match self {
138      AuditedResource::TcpPort(v) => v.fmt(f),
139      AuditedResource::UdpPort(v) => v.fmt(f),
140      AuditedResource::Url(v) => v.fmt(f),
141      AuditedResource::Volume(v) => v.fmt(f),
142    }
143  }
144}
145
146/// A summary of a UDP port resource.
147#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
148pub struct AuditedPort {
149  pub(crate) port: u16,
150  pub(crate) address: String,
151}
152
153impl std::fmt::Display for AuditedPort {
154  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155    write!(f, "{}:{}", self.address, self.port)
156  }
157}
158
159/// A summary of a volume resource.
160#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
161pub struct AuditedVolume {
162  pub(crate) path: PathBuf,
163}
164
165impl std::fmt::Display for AuditedVolume {
166  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167    f.write_str(&self.path.to_string_lossy())
168  }
169}
170
171/// A summary of a URL resource.
172#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize)]
173pub struct AuditedUrl {
174  pub(crate) url: Url,
175}
176
177impl From<Url> for AuditedUrl {
178  fn from(mut url: Url) -> Self {
179    let _ = url.set_username("");
180    let _ = url.set_password(None);
181    Self { url }
182  }
183}
184
185impl FromStr for AuditedUrl {
186  type Err = url::ParseError;
187
188  fn from_str(s: &str) -> Result<Self, Self::Err> {
189    let mut url = Url::parse(s)?;
190    let _ = url.set_username("");
191    let _ = url.set_password(None);
192
193    Ok(Self { url })
194  }
195}
196
197impl std::fmt::Display for AuditedUrl {
198  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199    f.write_str(self.url.as_str())
200  }
201}