grapl_graph_descriptions/
file.rs

1use 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}