malwaredb 0.3.2

Service for storing malicious, benign, or unknown files and related metadata and relationships.
// SPDX-License-Identifier: Apache-2.0

use malwaredb_server::State;

use std::process::ExitCode;

use anyhow::Result;
use clap::Parser;
use dialoguer::{Confirm, Password};

/// Create a new user
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct Create {
    /// Username
    #[arg(short, long)]
    pub uname: String,

    /// Email address
    #[arg(short, long)]
    pub email: String,

    /// First name
    #[arg(short, long)]
    pub fname: String,

    /// Last name
    #[arg(short, long)]
    pub lname: String,

    /// Organisation
    #[arg(short, long)]
    pub org: Option<String>,

    /// Prompt for the user's password (or leave empty)
    #[arg(short, long, default_value = "false")]
    pub password: bool,

    /// Create a read-only account, usually for guest access or demonstration
    #[arg(short, long, default_value_t = false)]
    pub readonly: bool,
}

impl Create {
    pub async fn execute(&self, state: State) -> Result<ExitCode> {
        let password = if self.password {
            let password = Password::new()
                .with_prompt(format!("New Password for {}", self.uname))
                .with_confirmation("Confirm password", "Passwords mismatching")
                .interact()?;
            Some(password)
        } else {
            None
        };

        state
            .db_type
            .create_user(
                &self.uname,
                &self.fname,
                &self.lname,
                &self.email,
                password,
                self.org.as_ref(),
                self.readonly,
            )
            .await?;

        Ok(ExitCode::SUCCESS)
    }
}

/// Reset (clear) all API tokens to force re-login
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct ResetAPIKeys {
    /// Bypass confirmation prompt
    #[arg(short, long, default_value = "false")]
    pub y: bool,
}

impl ResetAPIKeys {
    pub async fn execute(&self, state: State) -> Result<ExitCode> {
        if self.y
            || Confirm::new()
                .with_prompt("Do you want to continue?")
                .interact()?
        {
            let reset = state.db_type.reset_api_keys().await?;
            println!("Cleared {reset} API keys");
        } else {
            println!("Cancelled.");
        }

        Ok(ExitCode::SUCCESS)
    }
}

/// Change a user's password
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct ResetPassword {
    /// Username
    #[arg(short, long)]
    pub uname: String,
}

impl ResetPassword {
    pub async fn execute(&self, state: State) -> Result<ExitCode> {
        let password = Password::new()
            .with_prompt(format!("New Password for {}", self.uname))
            .with_confirmation("Confirm password", "Passwords mismatching")
            .interact()?;
        state.db_type.set_password(&self.uname, &password).await?;

        Ok(ExitCode::SUCCESS)
    }
}

/// List users
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct List {}

impl List {
    pub async fn execute(&self, state: State) -> Result<ExitCode> {
        for user in state.db_type.list_users().await? {
            println!("{user}");
        }

        Ok(ExitCode::SUCCESS)
    }
}