grapl_graph_descriptions/
file.rs1use std::convert::TryFrom;
2
3use log::warn;
4use serde_json::{json, Value};
5use uuid::Uuid;
6
7use crate::error::Error;
8use crate::graph_description::File;
9use crate::node::NodeT;
10
11#[derive(Debug, Clone)]
12pub enum FileState {
13 Created,
14 Deleted,
15 Existing,
16}
17
18impl TryFrom<u32> for FileState {
19 type Error = Error;
20
21 fn try_from(i: u32) -> Result<FileState, Error> {
22 match i {
23 1 => Ok(FileState::Created),
24 2 => Ok(FileState::Deleted),
25 3 => Ok(FileState::Existing),
26 _ => Err(Error::InvalidProcessState(i)),
27 }
28 }
29}
30
31impl From<FileState> for u32 {
32 fn from(p: FileState) -> u32 {
33 match p {
34 FileState::Created => 1,
35 FileState::Deleted => 2,
36 FileState::Existing => 3,
37 }
38 }
39}
40
41impl File {
42 pub fn new(
43 asset_id: impl Into<Option<String>>,
44 hostname: impl Into<Option<String>>,
45 state: FileState,
46 timestamp: u64,
47 file_name: String,
48 file_path: String,
49 file_extension: String,
50 file_mime_type: String,
51 file_size: u64,
52 file_version: String,
53 file_description: String,
54 file_product: String,
55 file_company: String,
56 file_directory: String,
57 file_inode: u64,
58 file_hard_links: u64,
59 md5_hash: String,
60 sha1_hash: String,
61 sha256_hash: String,
62 ) -> File {
63 let asset_id = asset_id.into();
64 let hostname = hostname.into();
65
66 if asset_id.is_none() && hostname.is_none() {
67 panic!("AssetID or Hostname must be provided for ProcessOutboundConnection");
68 }
69
70 let mut fd = File {
71 node_key: Uuid::new_v4().to_string(),
72 asset_id: asset_id.into(),
73 hostname: hostname.into(),
74 state: state.clone().into(),
75 created_timestamp: 0,
76 deleted_timestamp: 0,
77 last_seen_timestamp: 0,
78 file_name,
79 file_path,
80 file_extension,
81 file_mime_type,
82 file_size,
83 file_version,
84 file_description,
85 file_product,
86 file_company,
87 file_directory,
88 file_inode,
89 file_hard_links,
90 md5_hash,
91 sha1_hash,
92 sha256_hash,
93 };
94
95 match state {
96 FileState::Created => fd.created_timestamp = timestamp,
97 FileState::Existing => fd.last_seen_timestamp = timestamp,
98 FileState::Deleted => fd.deleted_timestamp = timestamp,
99 }
100
101 fd
102 }
103
104 pub fn into_json(self) -> Value {
105 let mut j = json!({
106 "node_key": self.node_key,
107 "dgraph.type": "File"
108 });
109
110 if !self.file_name.is_empty() {
111 j["file_name"] = Value::from(self.file_name);
112 }
113
114 if !self.file_path.is_empty() {
115 j["file_path"] = Value::from(self.file_path);
116 }
117
118 if !self.file_extension.is_empty() {
119 j["file_extension"] = Value::from(self.file_extension);
120 }
121
122 if !self.file_mime_type.is_empty() {
123 j["file_mime_type"] = Value::from(self.file_mime_type);
124 }
125
126 if self.file_size != 0 {
127 j["file_size"] = Value::from(self.file_size);
128 }
129
130 if !self.file_version.is_empty() {
131 j["file_version"] = Value::from(self.file_version);
132 }
133
134 if !self.file_description.is_empty() {
135 j["file_description"] = Value::from(self.file_description);
136 }
137
138 if !self.file_product.is_empty() {
139 j["file_product"] = Value::from(self.file_product);
140 }
141
142 if !self.file_company.is_empty() {
143 j["file_company"] = Value::from(self.file_company);
144 }
145
146 if !self.file_directory.is_empty() {
147 j["file_directory"] = Value::from(self.file_directory);
148 }
149
150 if self.file_inode != 0 {
151 j["file_inode"] = Value::from(self.file_inode);
152 }
153
154 if self.file_hard_links != 0 {
155 j["file_hard_links"] = Value::from(self.file_hard_links);
156 }
157
158 if !self.md5_hash.is_empty() {
159 j["md5_hash"] = Value::from(self.md5_hash);
160 }
161
162 if !self.sha1_hash.is_empty() {
163 j["sha1_hash"] = Value::from(self.sha1_hash);
164 }
165
166 if !self.sha256_hash.is_empty() {
167 j["sha256_hash"] = Value::from(self.sha256_hash);
168 }
169
170 if self.created_timestamp != 0 {
171 j["created_time"] = self.created_timestamp.into()
172 }
173
174 if self.deleted_timestamp != 0 {
175 j["deleted_timestamp"] = self.deleted_timestamp.into()
176 }
177 if self.last_seen_timestamp != 0 {
178 j["last_seen_timestamp"] = self.last_seen_timestamp.into()
179 }
180
181 j
182 }
183
184 pub fn timestamp(&self) -> u64 {
185 match FileState::try_from(self.state).unwrap() {
186 FileState::Created => self.created_timestamp,
187 FileState::Deleted => self.deleted_timestamp,
188 FileState::Existing => self.last_seen_timestamp,
189 }
190 }
191}
192
193impl NodeT for File {
194 fn get_asset_id(&self) -> Option<&str> {
195 self.asset_id.as_ref().map(String::as_str)
196 }
197
198 fn set_asset_id(&mut self, asset_id: impl Into<String>) {
199 self.asset_id = Some(asset_id.into());
200 }
201
202 fn get_node_key(&self) -> &str {
203 self.node_key.as_str()
204 }
205
206 fn set_node_key(&mut self, node_key: impl Into<String>) {
207 self.node_key = node_key.into()
208 }
209
210 fn merge(&mut self, other: &Self) -> bool {
211 if self.node_key != other.node_key {
212 warn!("Attempted to merge two file nodes with different keys. Dropping merge.");
213 return false;
214 }
215
216 let mut merged = false;
217
218 if self.asset_id.is_none() && other.asset_id.is_some() {
219 merged = true;
220 self.asset_id = other.asset_id.clone();
221 }
222
223 if self.hostname.is_none() && other.hostname.is_some() {
224 merged = true;
225 self.hostname = other.hostname.clone();
226 }
227
228 if self.file_name.is_empty() && !other.file_name.is_empty() {
229 merged = true;
230 self.file_name = other.file_name.clone();
231 }
232
233 if self.file_path.is_empty() && !other.file_path.is_empty() {
234 merged = true;
235 self.file_path = other.file_path.clone();
236 }
237
238 if self.file_extension.is_empty() && !other.file_extension.is_empty() {
239 merged = true;
240 self.file_extension = other.file_extension.clone();
241 }
242
243 if self.file_mime_type.is_empty() && !other.file_mime_type.is_empty() {
244 merged = true;
245 self.file_mime_type = other.file_mime_type.clone();
246 }
247
248 if self.file_size == 0 && other.file_size != 0 {
249 merged = true;
250 self.file_size = other.file_size;
251 }
252
253 if self.file_version.is_empty() && !other.file_version.is_empty() {
254 merged = true;
255 self.file_version = other.file_version.clone();
256 }
257
258 if self.file_description.is_empty() && !other.file_description.is_empty() {
259 merged = true;
260 self.file_description = other.file_description.clone();
261 }
262
263 if self.file_product.is_empty() && !other.file_product.is_empty() {
264 merged = true;
265 self.file_product = other.file_product.clone();
266 }
267
268 if self.file_company.is_empty() && !other.file_company.is_empty() {
269 merged = true;
270 self.file_company = other.file_company.clone();
271 }
272
273 if self.file_directory.is_empty() && !other.file_directory.is_empty() {
274 merged = true;
275 self.file_directory = other.file_directory.clone();
276 }
277
278 if self.file_inode == 0 && !other.file_inode != 0 {
279 merged = true;
280 self.file_inode = other.file_inode;
281 }
282
283 if self.file_hard_links == 0 && !other.file_hard_links != 0 {
284 merged = true;
285 self.file_hard_links = other.file_hard_links;
286 }
287
288 if self.md5_hash.is_empty() && !other.md5_hash.is_empty() {
289 merged = true;
290 self.md5_hash = other.md5_hash.clone();
291 }
292
293 if self.sha1_hash.is_empty() && !other.sha1_hash.is_empty() {
294 merged = true;
295 self.sha1_hash = other.sha1_hash.clone();
296 }
297
298 if self.sha256_hash.is_empty() && !other.sha256_hash.is_empty() {
299 merged = true;
300 self.sha256_hash = other.sha256_hash.clone();
301 }
302
303 if self.created_timestamp == 0 {
304 merged = true;
305 self.created_timestamp = other.created_timestamp;
306 }
307 if self.deleted_timestamp == 0 {
308 merged = true;
309 self.deleted_timestamp = other.deleted_timestamp;
310 }
311 if self.last_seen_timestamp == 0 {
312 merged = true;
313 self.last_seen_timestamp = other.last_seen_timestamp;
314 }
315
316 merged
317 }
318
319 fn merge_into(&mut self, other: Self) -> bool {
320 if self.node_key != other.node_key {
321 warn!("Attempted to merge two file nodes with different keys. Dropping merge.");
322 return false;
323 }
324
325 let mut merged = false;
326
327 if self.asset_id.is_none() && other.asset_id.is_some() {
328 merged = true;
329 self.asset_id = other.asset_id;
330 }
331
332 if self.hostname.is_none() && other.hostname.is_some() {
333 merged = true;
334 self.hostname = other.hostname;
335 }
336
337 if self.file_name.is_empty() && !other.file_name.is_empty() {
338 merged = true;
339 self.file_name = other.file_name;
340 }
341
342 if self.file_path.is_empty() && !other.file_path.is_empty() {
343 merged = true;
344 self.file_path = other.file_path;
345 }
346
347 if self.file_extension.is_empty() && !other.file_extension.is_empty() {
348 merged = true;
349 self.file_extension = other.file_extension;
350 }
351
352 if self.file_mime_type.is_empty() && !other.file_mime_type.is_empty() {
353 merged = true;
354 self.file_mime_type = other.file_mime_type;
355 }
356
357 if self.file_size == 0 && other.file_size != 0 {
358 merged = true;
359 self.file_size = other.file_size;
360 }
361
362 if self.file_version.is_empty() && !other.file_version.is_empty() {
363 merged = true;
364 self.file_version = other.file_version;
365 }
366
367 if self.file_description.is_empty() && !other.file_description.is_empty() {
368 merged = true;
369 self.file_description = other.file_description;
370 }
371
372 if self.file_product.is_empty() && !other.file_product.is_empty() {
373 merged = true;
374 self.file_product = other.file_product;
375 }
376
377 if self.file_company.is_empty() && !other.file_company.is_empty() {
378 merged = true;
379 self.file_company = other.file_company;
380 }
381
382 if self.file_directory.is_empty() && !other.file_directory.is_empty() {
383 merged = true;
384 self.file_directory = other.file_directory;
385 }
386
387 if self.file_inode == 0 && !other.file_inode != 0 {
388 merged = true;
389 self.file_inode = other.file_inode;
390 }
391
392 if self.file_hard_links == 0 && !other.file_hard_links != 0 {
393 merged = true;
394 self.file_hard_links = other.file_hard_links;
395 }
396
397 if self.md5_hash.is_empty() && !other.md5_hash.is_empty() {
398 merged = true;
399 self.md5_hash = other.md5_hash;
400 }
401
402 if self.sha1_hash.is_empty() && !other.sha1_hash.is_empty() {
403 merged = true;
404 self.sha1_hash = other.sha1_hash;
405 }
406
407 if self.sha256_hash.is_empty() && !other.sha256_hash.is_empty() {
408 merged = true;
409 self.sha256_hash = other.sha256_hash;
410 }
411
412 if self.created_timestamp == 0 {
413 merged = true;
414 self.created_timestamp = other.created_timestamp;
415 }
416 if self.deleted_timestamp == 0 {
417 merged = true;
418 self.deleted_timestamp = other.deleted_timestamp;
419 }
420 if self.last_seen_timestamp == 0 {
421 merged = true;
422 self.last_seen_timestamp = other.last_seen_timestamp;
423 }
424
425 merged
426 }
427}