Do not automatically create accounts; use enum to show this instead. #75
|
@ -1,13 +1,13 @@
|
||||||
use super::{Command, Execution, ExecutionResult};
|
use super::{Command, Execution, ExecutionError, ExecutionResult};
|
||||||
use crate::db::Users;
|
use crate::db::Users;
|
||||||
use crate::error::BotError::{AccountDoesNotExist, AuthenticationError, PasswordCreationError};
|
use crate::error::BotError::{AccountDoesNotExist, PasswordCreationError};
|
||||||
use crate::logic::hash_password;
|
use crate::logic::hash_password;
|
||||||
use crate::models::{AccountStatus, User};
|
use crate::models::{AccountStatus, User};
|
||||||
use crate::{context::Context, error::BotError};
|
use crate::{context::Context, error::BotError};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::convert::{Into, TryFrom};
|
use std::convert::{Into, TryFrom};
|
||||||
|
|
||||||
pub struct RegisterCommand(pub String);
|
pub struct RegisterCommand;
|
||||||
|
|
||||||
impl From<RegisterCommand> for Box<dyn Command> {
|
impl From<RegisterCommand> for Box<dyn Command> {
|
||||||
fn from(cmd: RegisterCommand) -> Self {
|
fn from(cmd: RegisterCommand) -> Self {
|
||||||
|
@ -18,8 +18,8 @@ impl From<RegisterCommand> for Box<dyn Command> {
|
||||||
impl TryFrom<&str> for RegisterCommand {
|
impl TryFrom<&str> for RegisterCommand {
|
||||||
type Error = BotError;
|
type Error = BotError;
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
fn try_from(_: &str) -> Result<Self, Self::Error> {
|
||||||
Ok(RegisterCommand(value.to_owned()))
|
Ok(RegisterCommand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,24 +34,114 @@ impl Command for RegisterCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
||||||
let pw_hash = hash_password(&self.0).map_err(|e| PasswordCreationError(e))?;
|
if let Some(_) = ctx.db.get_user(ctx.username).await? {
|
||||||
|
return Err(ExecutionError(BotError::AccountAlreadyExists));
|
||||||
|
}
|
||||||
|
|
||||||
let user = User {
|
let user = User {
|
||||||
username: ctx.username.to_owned(),
|
username: ctx.username.to_owned(),
|
||||||
password: Some(pw_hash),
|
password: None,
|
||||||
account_status: AccountStatus::Registered,
|
account_status: AccountStatus::Registered,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.db.upsert_user(&user).await?;
|
ctx.db.upsert_user(&user).await?;
|
||||||
Execution::success(format!(
|
Execution::success(format!(
|
||||||
"User account registered/updated. Please log in to external applications \
|
"User account {} registered for bot commands.",
|
||||||
with username {} and the password you set.",
|
|
||||||
ctx.username
|
ctx.username
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CheckCommand(pub String);
|
pub struct UnlinkCommand(pub String);
|
||||||
|
|
||||||
|
impl From<UnlinkCommand> for Box<dyn Command> {
|
||||||
|
fn from(cmd: UnlinkCommand) -> Self {
|
||||||
|
Box::new(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for UnlinkCommand {
|
||||||
|
type Error = BotError;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
Ok(UnlinkCommand(value.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Command for UnlinkCommand {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"unlink user accountx from external applications"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_secure(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
||||||
|
let mut user = ctx
|
||||||
|
.db
|
||||||
|
.get_user(&ctx.username)
|
||||||
|
.await?
|
||||||
|
.ok_or(BotError::AccountDoesNotExist)?;
|
||||||
|
|
||||||
|
user.password = None;
|
||||||
|
ctx.db.upsert_user(&user).await?;
|
||||||
|
|
||||||
|
Execution::success(format!(
|
||||||
|
"Accounted {} is now inaccessible to external applications.",
|
||||||
|
ctx.username
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LinkCommand(pub String);
|
||||||
|
|
||||||
|
impl From<LinkCommand> for Box<dyn Command> {
|
||||||
|
fn from(cmd: LinkCommand) -> Self {
|
||||||
|
Box::new(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for LinkCommand {
|
||||||
|
type Error = BotError;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
Ok(LinkCommand(value.to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Command for LinkCommand {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"link user account to external applications"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_secure(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
||||||
|
let mut user = ctx
|
||||||
|
.db
|
||||||
|
.get_user(&ctx.username)
|
||||||
|
.await?
|
||||||
|
.ok_or(BotError::AccountDoesNotExist)?;
|
||||||
|
|
||||||
|
let pw_hash = hash_password(&self.0).map_err(|e| PasswordCreationError(e))?;
|
||||||
|
user.password = Some(pw_hash);
|
||||||
|
ctx.db.upsert_user(&user).await?;
|
||||||
|
|
||||||
|
Execution::success(format!(
|
||||||
|
"Accounted now available for external use. Please log in to \
|
||||||
|
external applications with username {} and the password you set.",
|
||||||
|
ctx.username
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CheckCommand;
|
||||||
|
|
||||||
impl From<CheckCommand> for Box<dyn Command> {
|
impl From<CheckCommand> for Box<dyn Command> {
|
||||||
fn from(cmd: CheckCommand) -> Self {
|
fn from(cmd: CheckCommand) -> Self {
|
||||||
|
@ -62,15 +152,15 @@ impl From<CheckCommand> for Box<dyn Command> {
|
||||||
impl TryFrom<&str> for CheckCommand {
|
impl TryFrom<&str> for CheckCommand {
|
||||||
type Error = BotError;
|
type Error = BotError;
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
fn try_from(_: &str) -> Result<Self, Self::Error> {
|
||||||
Ok(CheckCommand(value.to_owned()))
|
Ok(CheckCommand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for CheckCommand {
|
impl Command for CheckCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"check user password"
|
"check user account status"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_secure(&self) -> bool {
|
fn is_secure(&self) -> bool {
|
||||||
|
@ -78,11 +168,20 @@ impl Command for CheckCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult {
|
||||||
let user = ctx.db.authenticate_user(&ctx.username, &self.0).await?;
|
let user = ctx.db.get_user(&ctx.username).await?;
|
||||||
|
|
||||||
match user {
|
match user {
|
||||||
Some(_) => Execution::success("Password is correct!".to_string()),
|
Some(user) => match user.password {
|
||||||
None => Err(AuthenticationError.into()),
|
Some(_) => Execution::success(
|
||||||
|
"Account exists, and is available to external applications with a password. If you forgot your password, change it with !link.".to_string(),
|
||||||
|
),
|
||||||
|
None => Execution::success(
|
||||||
|
"Account exists, but is not available to external applications.".to_string(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
None => Execution::success(
|
||||||
|
"No account registered. Only simple commands in public rooms are available.".to_string(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,7 +209,7 @@ mod tests {
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
|
||||||
let cmd = RegisterCommand("".to_owned());
|
let cmd = RegisterCommand;
|
||||||
assert_eq!(execution_allowed(&cmd, &ctx).is_ok(), true);
|
assert_eq!(execution_allowed(&cmd, &ctx).is_ok(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ mod tests {
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
|
||||||
let cmd = RegisterCommand("".to_owned());
|
let cmd = RegisterCommand;
|
||||||
assert_eq!(execution_allowed(&cmd, &ctx).is_err(), true);
|
assert_eq!(execution_allowed(&cmd, &ctx).is_err(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::commands::{
|
||||||
basic_rolling::RollCommand,
|
basic_rolling::RollCommand,
|
||||||
cofd::PoolRollCommand,
|
cofd::PoolRollCommand,
|
||||||
cthulhu::{CthAdvanceRoll, CthRoll},
|
cthulhu::{CthAdvanceRoll, CthRoll},
|
||||||
management::{CheckCommand, RegisterCommand, UnregisterCommand},
|
management::{CheckCommand, LinkCommand, RegisterCommand, UnlinkCommand, UnregisterCommand},
|
||||||
misc::HelpCommand,
|
misc::HelpCommand,
|
||||||
variables::{
|
variables::{
|
||||||
DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand,
|
DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand,
|
||||||
|
@ -86,6 +86,8 @@ pub fn parse_command(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||||
"cthadv" | "ctharoll" => convert_to!(CthAdvanceRoll, cmd_input),
|
"cthadv" | "ctharoll" => convert_to!(CthAdvanceRoll, cmd_input),
|
||||||
"help" => convert_to!(HelpCommand, cmd_input),
|
"help" => convert_to!(HelpCommand, cmd_input),
|
||||||
"register" => convert_to!(RegisterCommand, cmd_input),
|
"register" => convert_to!(RegisterCommand, cmd_input),
|
||||||
|
"link" => convert_to!(LinkCommand, cmd_input),
|
||||||
|
"unlink" => convert_to!(UnlinkCommand, cmd_input),
|
||||||
"check" => convert_to!(CheckCommand, cmd_input),
|
"check" => convert_to!(CheckCommand, cmd_input),
|
||||||
"unregister" => convert_to!(UnregisterCommand, cmd_input),
|
"unregister" => convert_to!(UnregisterCommand, cmd_input),
|
||||||
_ => Err(CommandParsingError::UnrecognizedCommand(cmd).into()),
|
_ => Err(CommandParsingError::UnrecognizedCommand(cmd).into()),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::db::sqlite::Database;
|
use crate::db::sqlite::Database;
|
||||||
use crate::error::BotError;
|
use crate::error::BotError;
|
||||||
use crate::models::{Account, User};
|
use crate::models::Account;
|
||||||
use matrix_sdk::identifiers::{RoomId, UserId};
|
use matrix_sdk::identifiers::{RoomId, UserId};
|
||||||
use matrix_sdk::room::Joined;
|
use matrix_sdk::room::Joined;
|
||||||
use matrix_sdk::Client;
|
use matrix_sdk::Client;
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::db::{errors::DataError, Users};
|
||||||
use crate::error::BotError;
|
use crate::error::BotError;
|
||||||
use crate::models::User;
|
use crate::models::User;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::info;
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Users for Database {
|
impl Users for Database {
|
||||||
|
|
|
@ -87,6 +87,9 @@ pub enum BotError {
|
||||||
|
|
||||||
#[error("user account does not exist, try registering")]
|
#[error("user account does not exist, try registering")]
|
||||||
AccountDoesNotExist,
|
AccountDoesNotExist,
|
||||||
|
|
||||||
|
#[error("user account already exists")]
|
||||||
|
AccountAlreadyExists,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
Loading…
Reference in New Issue