malwaredb-server 0.3.6

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

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

/// User record
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct User {
    /// User ID, for database relations
    pub id: u32,

    /// Email address
    pub email: String,

    /// Username, unique
    pub uname: String,

    /// First name
    pub fname: String,

    /// Last name
    pub lname: String,

    /// Organization, optional
    pub org: Option<String>,

    /// Phone number, optional
    pub phone: Option<String>,

    /// If a password is set
    pub has_password: bool,

    /// If the user has logged in and fetched an API key
    pub has_api_key: bool,

    /// Creation timestamp
    pub created: DateTime<Utc>,

    /// User is readonly
    pub is_readonly: bool,

    /// If the user account if for anonymous access
    #[cfg(feature = "anonymous")]
    pub is_anonymous_user: 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 { "" };

        let anon = {
            #[cfg(feature = "anonymous")]
            if self.is_anonymous_user {
                " anonymous access account"
            } else {
                ""
            }
            #[cfg(not(feature = "anonymous"))]
            ""
        };

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

/// Group record

#[derive(Clone, Debug)]
pub struct Group {
    /// Group ID, for database relations
    pub id: u32,

    /// Group name
    pub name: String,

    /// Optional description
    pub description: Option<String>,

    /// Parent group, if present
    pub parent: Option<String>,

    /// Members of the group
    pub members: Vec<User>,

    /// Sources available to the group
    pub sources: Vec<Source>,

    /// Number of files accessible by the group
    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(())
    }
}

/// Source of samples in Malware DB
#[derive(Clone, Debug, PartialEq, Eq)]
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: 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(())
    }
}