Add an account deletion command.

This commit is contained in:
projectmoon 2021-05-22 23:12:17 +00:00
parent 921c4cd644
commit 76214bc790
5 changed files with 71 additions and 2 deletions

View File

@ -1,7 +1,7 @@
use super::{Command, Execution, ExecutionResult}; use super::{Command, Execution, ExecutionResult};
use crate::context::Context; use crate::context::Context;
use crate::db::Users; 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::logic::{hash_password, record_room_information};
use crate::models::User; use crate::models::User;
use async_trait::async_trait; 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()),
}
}
}

View File

@ -9,7 +9,7 @@ use crate::commands::{
basic_rolling::RollCommand, basic_rolling::RollCommand,
cofd::PoolRollCommand, cofd::PoolRollCommand,
cthulhu::{CthAdvanceRoll, CthRoll}, cthulhu::{CthAdvanceRoll, CthRoll},
management::{CheckCommand, RegisterCommand, ResyncCommand}, management::{CheckCommand, RegisterCommand, ResyncCommand, UnregisterCommand},
misc::HelpCommand, misc::HelpCommand,
variables::{ variables::{
DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand, DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand,
@ -55,6 +55,10 @@ fn parse_check_command(input: &str) -> Result<Box<dyn Command>, BotError> {
Ok(Box::new(CheckCommand(input.to_owned()))) Ok(Box::new(CheckCommand(input.to_owned())))
} }
fn parse_unregister_command() -> Result<Box<dyn Command>, BotError> {
Ok(Box::new(UnregisterCommand))
}
fn parse_get_variable_command(input: &str) -> Result<Box<dyn Command>, BotError> { fn parse_get_variable_command(input: &str) -> Result<Box<dyn Command>, BotError> {
Ok(Box::new(GetVariableCommand(input.to_owned()))) Ok(Box::new(GetVariableCommand(input.to_owned())))
} }
@ -151,6 +155,7 @@ pub fn parse_command(input: &str) -> Result<Box<dyn Command>, BotError> {
"help" => help(&cmd_input), "help" => help(&cmd_input),
"register" => parse_register_command(&cmd_input), "register" => parse_register_command(&cmd_input),
"check" => parse_check_command(&cmd_input), "check" => parse_check_command(&cmd_input),
"unregister" => parse_unregister_command(),
_ => Err(CommandParsingError::UnrecognizedCommand(cmd).into()), _ => Err(CommandParsingError::UnrecognizedCommand(cmd).into()),
}, },
//All other errors passed up. //All other errors passed up.

View File

@ -22,6 +22,8 @@ pub(crate) trait Users {
async fn get_user(&self, username: &str) -> Result<Option<User>, DataError>; async fn get_user(&self, username: &str) -> Result<Option<User>, DataError>;
async fn delete_user(&self, username: &str) -> Result<(), DataError>;
async fn authenticate_user( async fn authenticate_user(
&self, &self,
username: &str, username: &str,

View File

@ -20,6 +20,15 @@ impl Users for Database {
Ok(()) 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<Option<User>, DataError> { async fn get_user(&self, username: &str) -> Result<Option<User>, DataError> {
let user_row = sqlx::query!( let user_row = sqlx::query!(
r#"SELECT user_id, password FROM accounts r#"SELECT user_id, password FROM accounts
@ -119,6 +128,31 @@ mod tests {
assert_eq!(user.password, "123"); //From second upsert 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)] #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn username_not_in_db_returns_none() { async fn username_not_in_db_returns_none() {
let db = create_db().await; let db = create_db().await;

View File

@ -84,6 +84,9 @@ pub enum BotError {
#[error("account does not exist, or password incorrect")] #[error("account does not exist, or password incorrect")]
AuthenticationError, AuthenticationError,
#[error("user account does not exist, try registering")]
AccountDoesNotExist,
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]