diff --git a/src/commands/management.rs b/src/commands/management.rs index ce3a704..f4396d2 100644 --- a/src/commands/management.rs +++ b/src/commands/management.rs @@ -33,3 +33,20 @@ impl Command for ResyncCommand { Execution::success(message) } } + +pub struct RegisterCommand(pub String); + +#[async_trait] +impl Command for RegisterCommand { + fn name(&self) -> &'static str { + "register user account" + } + + fn is_secure(&self) -> bool { + true + } + + async fn execute(&self, ctx: &Context<'_>) -> ExecutionResult { + Execution::success("User account registered".to_string()) + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index e226402..631e4ad 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -19,6 +19,9 @@ pub enum CommandError { #[error("invalid command: {0}")] InvalidCommand(String), + #[error("command can only be executed from encrypted direct message")] + InsecureExecution, + #[error("ignored command")] IgnoredCommand, } @@ -99,13 +102,33 @@ pub trait Command: Send + Sync { fn is_secure(&self) -> bool; } +/// Determine if we are allowed to execute this command. Currently the +/// rules are that secure commands must be executed in secure rooms +/// (encrypted + direct), and anything else can be executed where +/// ever. Later, we can add stuff like admin/regular user power +/// separation, etc. +fn execution_allowed(cmd: &(impl Command + ?Sized), ctx: &Context<'_>) -> Result<(), CommandError> { + if cmd.is_secure() { + if ctx.is_secure() { + Ok(()) + } else { + Err(CommandError::InsecureExecution) + } + } else { + Ok(()) + } +} /// Attempt to execute a command, and return the content that should /// go back to Matrix, if the command was executed (successfully or /// not). If a command is determined to be ignored, this function will /// return None, signifying that we should not send a response. pub async fn execute_command(ctx: &Context<'_>) -> ExecutionResult { let cmd = parser::parse_command(&ctx.message_body)?; - cmd.execute(ctx).await + + match execution_allowed(cmd.as_ref(), ctx) { + Ok(_) => cmd.execute(ctx).await, + Err(e) => Err(ExecutionError(e.into())), + } } #[cfg(test)] diff --git a/src/commands/parser.rs b/src/commands/parser.rs index 8ff52ba..2e6874d 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::ResyncCommand, + management::{RegisterCommand, ResyncCommand}, misc::HelpCommand, variables::{ DeleteVariableCommand, GetAllVariablesCommand, GetVariableCommand, SetVariableCommand, @@ -47,6 +47,10 @@ fn parse_roll(input: &str) -> Result, BotError> { } } +fn parse_register_command(input: &str) -> Result, BotError> { + Ok(Box::new(RegisterCommand(input.to_owned()))) +} + fn parse_get_variable_command(input: &str) -> Result, BotError> { Ok(Box::new(GetVariableCommand(input.to_owned()))) } @@ -141,6 +145,7 @@ pub fn parse_command(input: &str) -> Result, BotError> { "cthadv" | "ctharoll" => parse_cth_advancement_roll(&cmd_input), "chance" => chance_die(), "help" => help(&cmd_input), + "register" => parse_register_command(&cmd_input), _ => Err(CommandParsingError::UnrecognizedCommand(cmd).into()), }, //All other errors passed up. diff --git a/src/db/sqlite/migrator/migrations/V6__user_accounts.rs b/src/db/sqlite/migrator/migrations/V6__user_accounts.rs new file mode 100644 index 0000000..41c009e --- /dev/null +++ b/src/db/sqlite/migrator/migrations/V6__user_accounts.rs @@ -0,0 +1,18 @@ +use barrel::backend::Sqlite; +use barrel::{types, types::Type, Migration}; + +fn primary_uuid() -> Type { + types::text().unique(true).primary(true).nullable(false) +} + +pub fn migration() -> String { + let mut m = Migration::new(); + + //Table of room ID, event ID, event timestamp + m.create_table("accounts", move |t| { + t.add_column("user_id", primary_uuid()); + t.add_column("password", types::text().nullable(false)); + }); + + m.make::() +}