From 76214bc79060e72b2e7a594f95fb02871d36f3c1 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Sat, 22 May 2021 23:12:17 +0000 Subject: [PATCH] Add an account deletion command. --- src/commands/management.rs | 27 ++++++++++++++++++++++++++- src/commands/parser.rs | 7 ++++++- src/db/mod.rs | 2 ++ src/db/sqlite/users.rs | 34 ++++++++++++++++++++++++++++++++++ src/error.rs | 3 +++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/commands/management.rs b/src/commands/management.rs index 571a75d..0f1eab9 100644 --- a/src/commands/management.rs +++ b/src/commands/management.rs @@ -1,7 +1,7 @@ use super::{Command, Execution, ExecutionResult}; use crate::context::Context; use crate::db::Users; -use crate::error::BotError::{AuthenticationError, PasswordCreationError}; +use crate::error::BotError::{AccountDoesNotExist, AuthenticationError, PasswordCreationError}; use crate::logic::{hash_password, record_room_information}; use crate::models::User; use async_trait::async_trait; @@ -82,3 +82,28 @@ impl Command for CheckCommand { } } } + +pub struct UnregisterCommand; + +#[async_trait] +impl Command for UnregisterCommand { + fn name(&self) -> &'static str { + "unregister user account" + } + + fn is_secure(&self) -> bool { + true + } + + async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult { + let user = ctx.db.get_user(&ctx.username).await?; + + match user { + Some(_) => { + ctx.db.delete_user(&ctx.username).await?; + Execution::success("Your user account has been removed.".to_string()) + } + None => Err(AccountDoesNotExist.into()), + } + } +} diff --git a/src/commands/parser.rs b/src/commands/parser.rs index 2ab0837..327ca6b 100644 --- a/src/commands/parser.rs +++ b/src/commands/parser.rs @@ -9,7 +9,7 @@ use crate::commands::{ basic_rolling::RollCommand, cofd::PoolRollCommand, cthulhu::{CthAdvanceRoll, CthRoll}, - management::{CheckCommand, RegisterCommand, ResyncCommand}, + management::{CheckCommand, RegisterCommand, ResyncCommand, UnregisterCommand}, misc::HelpCommand, variables::{ DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand, @@ -55,6 +55,10 @@ fn parse_check_command(input: &str) -> Result, BotError> { Ok(Box::new(CheckCommand(input.to_owned()))) } +fn parse_unregister_command() -> Result, BotError> { + Ok(Box::new(UnregisterCommand)) +} + fn parse_get_variable_command(input: &str) -> Result, BotError> { Ok(Box::new(GetVariableCommand(input.to_owned()))) } @@ -151,6 +155,7 @@ pub fn parse_command(input: &str) -> Result, BotError> { "help" => help(&cmd_input), "register" => parse_register_command(&cmd_input), "check" => parse_check_command(&cmd_input), + "unregister" => parse_unregister_command(), _ => Err(CommandParsingError::UnrecognizedCommand(cmd).into()), }, //All other errors passed up. diff --git a/src/db/mod.rs b/src/db/mod.rs index 6e916f1..e3aac77 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -22,6 +22,8 @@ pub(crate) trait Users { async fn get_user(&self, username: &str) -> Result, DataError>; + async fn delete_user(&self, username: &str) -> Result<(), DataError>; + async fn authenticate_user( &self, username: &str, diff --git a/src/db/sqlite/users.rs b/src/db/sqlite/users.rs index a297efc..23b1dac 100644 --- a/src/db/sqlite/users.rs +++ b/src/db/sqlite/users.rs @@ -20,6 +20,15 @@ impl Users for Database { Ok(()) } + async fn delete_user(&self, username: &str) -> Result<(), DataError> { + sqlx::query(r#"DELETE FROM accounts WHERE user_id = ?"#) + .bind(&username) + .execute(&self.conn) + .await?; + + Ok(()) + } + async fn get_user(&self, username: &str) -> Result, DataError> { let user_row = sqlx::query!( r#"SELECT user_id, password FROM accounts @@ -119,6 +128,31 @@ mod tests { assert_eq!(user.password, "123"); //From second upsert } + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn can_delete_user() { + let db = create_db().await; + + let insert_result = db + .upsert_user(&User { + username: "myuser".to_string(), + password: "abc".to_string(), + }) + .await; + + assert!(insert_result.is_ok()); + + db.delete_user("myuser") + .await + .expect("User deletion query failed"); + + let user = db + .get_user("myuser") + .await + .expect("User retrieval query failed"); + + assert!(user.is_none()); + } + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn username_not_in_db_returns_none() { let db = create_db().await; diff --git a/src/error.rs b/src/error.rs index 65182d1..46de875 100644 --- a/src/error.rs +++ b/src/error.rs @@ -84,6 +84,9 @@ pub enum BotError { #[error("account does not exist, or password incorrect")] AuthenticationError, + + #[error("user account does not exist, try registering")] + AccountDoesNotExist, } #[derive(Error, Debug)]