malwaredb-server 0.3.4

Server data storage logic for MalwareDB.
Documentation
// SPDX-License-Identifier: Apache-2.0

use chrono::{DateTime, Utc};
use std::fmt::{Display, Formatter};

#[derive(Clone, Debug)]
pub struct User {
    pub id: u32,
    pub email: String,
    pub uname: String,
    pub fname: String,
    pub lname: String,
    pub org: Option<String>,
    pub phone: Option<String>,
    pub has_password: bool,
    pub has_api_key: bool,
    pub created: DateTime<Utc>,
    pub is_readonly: bool,
}

impl Display for User {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let has_password = {
            if self.has_password {
                "password set"
            } else {
                "no password"
            }
        };
        let has_api_key = {
            if self.has_api_key {
                "API key set"
            } else {
                "no API key"
            }
        };
        let org = {
            if let Some(org) = &self.org {
                format!(" ({org}) ")
            } else {
                String::new()
            }
        };
        let phone = {
            if let Some(ph) = &self.phone {
                format!(" {ph} ")
            } else {
                String::new()
            }
        };

        let readonly = if self.is_readonly { " read only" } else { "" };

        write!(
            f,
            "{}: {} {} {}<{}>,{phone}{org}{has_password}, {has_api_key} created {}{readonly}",
            self.id, self.fname, self.lname, self.uname, self.email, self.created
        )
    }
}

#[derive(Clone, Debug)]
pub struct Group {
    pub id: u32,
    pub name: String,
    pub description: Option<String>,
    pub parent: Option<String>,
    pub members: Vec<User>,
    pub sources: Vec<Source>,
    pub files: u32,
}

impl Display for Group {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let parent = if let Some(p) = &self.parent {
            format!("Parent: {p}")
        } else {
            String::new()
        };

        let sources_label = match self.sources.len() {
            0 => "no sources".into(),
            1 => "one source".into(),
            x => format!("{x} sources:"),
        };

        let members_label = match self.members.len() {
            0 => "no members".into(),
            1 => "one member".into(),
            x => format!("{x} members:"),
        };

        if let Some(desc) = &self.description {
            writeln!(
                f,
                "{}: {} {parent} {} files -- {desc} {members_label} {sources_label}",
                self.id, self.name, self.files
            )?;
        } else {
            writeln!(
                f,
                "{}: {} {parent} {} files -- {members_label} {sources_label}",
                self.id, self.name, self.files
            )?;
        }

        writeln!(f, "Members:")?;
        for user in &self.members {
            writeln!(f, "\t{user}")?;
        }

        writeln!(f, "Sources:")?;
        for source in &self.sources {
            writeln!(f, "\t{source}")?;
        }

        Ok(())
    }
}

#[derive(Clone, Debug)]
pub struct Source {
    /// ID of the source
    pub id: u32,

    /// Name of the source
    pub name: String,

    /// Description of the source
    pub description: Option<String>,

    /// Website of the source, or where the source's data may be found
    pub url: Option<String>,

    /// Date of first acquisition from the source, or creation date of the source
    pub date: chrono::DateTime<chrono::Local>,

    /// Name of the parent source
    pub parent: Option<String>,

    /// Amount of files originating from this source
    pub files: u64,

    /// Number of groups which may access this source
    pub groups: u32,

    /// Whether this source is known to host malware
    pub malicious: Option<bool>,
}

impl Display for Source {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}: {}", self.id, self.name)?;
        if let Some(url) = &self.url {
            write!(f, " {url}")?;
        }
        if let Some(desc) = &self.description {
            write!(f, " {desc}")?;
        }
        if let Some(is_malicious) = &self.malicious {
            write!(f, ", known malicious {is_malicious},")?;
        }
        if let Some(parent) = &self.parent {
            write!(f, ", parent {parent},")?;
        }
        write!(
            f,
            " {} files and {} groups from {}",
            self.files, self.groups, self.date
        )?;
        Ok(())
    }
}