use malwaredb_server::State;
use std::process::ExitCode;
use anyhow::Result;
use clap::Parser;
use dialoguer::{Confirm, Password};
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct Create {
#[arg(short, long)]
pub uname: String,
#[arg(short, long)]
pub email: String,
#[arg(short, long)]
pub fname: String,
#[arg(short, long)]
pub lname: String,
#[arg(short, long)]
pub org: Option<String>,
#[arg(short, long, default_value = "false")]
pub password: bool,
#[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)
}
}
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct ResetAPIKeys {
#[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)
}
}
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct ResetPassword {
#[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)
}
}
#[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)
}
}
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct ToggleReadOnly {
pub uid: u32,
}
impl ToggleReadOnly {
pub async fn execute(&self, state: State) -> Result<ExitCode> {
let uinfo = state.db_type.get_user_info(self.uid).await?;
if uinfo.is_readonly {
state.db_type.set_user_rw(self.uid).await?;
println!("{} is now read-write", uinfo.username);
} else {
state.db_type.set_user_ro(self.uid).await?;
println!("{} is now read-only", uinfo.username);
}
Ok(ExitCode::SUCCESS)
}
}
#[cfg(feature = "anonymous")]
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct SpecifyAnonymousUser {
pub uid: u32,
}
#[cfg(feature = "anonymous")]
impl SpecifyAnonymousUser {
pub async fn execute(&self, state: State) -> Result<ExitCode> {
if let Some(uid) = state.db_config.anonymous_uid {
let uname = state.db_type.get_user_info(uid).await?.username;
eprintln!("Current anonymous user is {uname} with uid {uid}");
}
let anon_info = state.db_type.get_user_info(self.uid).await?;
if anon_info.is_admin {
eprintln!("Refusing to allow an admin account to be used for anonymous connections!");
return Ok(ExitCode::FAILURE);
}
state.db_type.set_anonymous_user(self.uid).await?;
println!("Anonymous access enabled as user {}", anon_info.username);
Ok(ExitCode::SUCCESS)
}
}
#[cfg(feature = "anonymous")]
pub async fn clear_anonymous_user(state: State) -> Result<ExitCode> {
state.db_type.clear_anonymous_user().await?;
println!("Anonymous user access disabled");
Ok(ExitCode::SUCCESS)
}