From 8c2a90e86bd5ac29be42de2d294d43b2fa04146d Mon Sep 17 00:00:00 2001 From: projectmoon Date: Sat, 22 May 2021 22:48:47 +0000 Subject: [PATCH] Tests for secure commands and user DB API. --- src/commands/mod.rs | 96 ++++++++++++++++++++++++++++++++++ src/db/sqlite/users.rs | 114 +++++++++++++++++++++++++++++++++-------- src/models.rs | 25 +++++++++ 3 files changed, 215 insertions(+), 20 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 63675b0..16624ae 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -168,6 +168,7 @@ fn log_command(cmd: &(impl Command + ?Sized), ctx: &Context, result: &ExecutionR #[cfg(test)] mod tests { use super::*; + use management::RegisterCommand; use url::Url; macro_rules! dummy_room { @@ -180,6 +181,100 @@ mod tests { }; } + macro_rules! secure_room { + () => { + crate::context::RoomContext { + id: &matrix_sdk::identifiers::room_id!("!fakeroomid:example.com"), + display_name: "displayname".to_owned(), + secure: true, + } + }; + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn secure_context_secure_command_allows_execution() { + let db_path = tempfile::NamedTempFile::new_in(".").unwrap(); + let db = crate::db::sqlite::Database::new(db_path.path().to_str().unwrap()) + .await + .unwrap(); + + let homeserver = Url::parse("http://example.com").unwrap(); + + let ctx = Context { + db: db, + matrix_client: &matrix_sdk::Client::new(homeserver).unwrap(), + room: secure_room!(), + username: "myusername", + message_body: "!notacommand", + }; + + let cmd = RegisterCommand("".to_owned()); + assert_eq!(execution_allowed(&cmd, &ctx).is_ok(), true); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn secure_context_insecure_command_allows_execution() { + let db_path = tempfile::NamedTempFile::new_in(".").unwrap(); + let db = crate::db::sqlite::Database::new(db_path.path().to_str().unwrap()) + .await + .unwrap(); + + let homeserver = Url::parse("http://example.com").unwrap(); + + let ctx = Context { + db: db, + matrix_client: &matrix_sdk::Client::new(homeserver).unwrap(), + room: secure_room!(), + username: "myusername", + message_body: "!notacommand", + }; + + let cmd = variables::GetVariableCommand("".to_owned()); + assert_eq!(execution_allowed(&cmd, &ctx).is_ok(), true); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn insecure_context_insecure_command_allows_execution() { + let db_path = tempfile::NamedTempFile::new_in(".").unwrap(); + let db = crate::db::sqlite::Database::new(db_path.path().to_str().unwrap()) + .await + .unwrap(); + + let homeserver = Url::parse("http://example.com").unwrap(); + + let ctx = Context { + db: db, + matrix_client: &matrix_sdk::Client::new(homeserver).unwrap(), + room: dummy_room!(), + username: "myusername", + message_body: "!notacommand", + }; + + let cmd = variables::GetVariableCommand("".to_owned()); + assert_eq!(execution_allowed(&cmd, &ctx).is_ok(), true); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn insecure_context_secure_command_denies_execution() { + let db_path = tempfile::NamedTempFile::new_in(".").unwrap(); + let db = crate::db::sqlite::Database::new(db_path.path().to_str().unwrap()) + .await + .unwrap(); + + let homeserver = Url::parse("http://example.com").unwrap(); + + let ctx = Context { + db: db, + matrix_client: &matrix_sdk::Client::new(homeserver).unwrap(), + room: dummy_room!(), + username: "myusername", + message_body: "!notacommand", + }; + + let cmd = RegisterCommand("".to_owned()); + assert_eq!(execution_allowed(&cmd, &ctx).is_err(), true); + } + #[test] fn command_result_extractor_creates_bubble() { let result = Execution::success("test".to_string()); @@ -205,6 +300,7 @@ mod tests { username: "myusername", message_body: "!notacommand", }; + let result = execute_command(&ctx).await; assert!(result.is_err()); } diff --git a/src/db/sqlite/users.rs b/src/db/sqlite/users.rs index 78c6793..a297efc 100644 --- a/src/db/sqlite/users.rs +++ b/src/db/sqlite/users.rs @@ -47,8 +47,9 @@ impl Users for Database { #[cfg(test)] mod tests { + use super::*; use crate::db::sqlite::Database; - use crate::db::DbState; + use crate::db::Users; async fn create_db() -> Database { let db_path = tempfile::NamedTempFile::new_in(".").unwrap(); @@ -62,41 +63,114 @@ mod tests { } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn set_and_get_device_id() { + async fn create_and_get_user_test() { let db = create_db().await; - db.set_device_id("device_id") + let insert_result = db + .upsert_user(&User { + username: "myuser".to_string(), + password: "abc".to_string(), + }) + .await; + + assert!(insert_result.is_ok()); + + let user = db + .get_user("myuser") .await - .expect("Could not set device ID"); + .expect("User retrieval query failed"); - let device_id = db.get_device_id().await.expect("Could not get device ID"); - - assert!(device_id.is_some()); - assert_eq!(device_id.unwrap(), "device_id"); + assert!(user.is_some()); + let user = user.unwrap(); + assert_eq!(user.username, "myuser"); + assert_eq!(user.password, "abc"); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn no_device_id_set_returns_none() { + async fn can_update_user() { let db = create_db().await; - let device_id = db.get_device_id().await.expect("Could not get device ID"); - assert!(device_id.is_none()); + + let insert_result1 = db + .upsert_user(&User { + username: "myuser".to_string(), + password: "abc".to_string(), + }) + .await; + + assert!(insert_result1.is_ok()); + + let insert_result2 = db + .upsert_user(&User { + username: "myuser".to_string(), + password: "123".to_string(), + }) + .await; + + assert!(insert_result2.is_ok()); + + let user = db + .get_user("myuser") + .await + .expect("User retrieval query failed"); + + assert!(user.is_some()); + let user = user.unwrap(); + assert_eq!(user.username, "myuser"); + assert_eq!(user.password, "123"); //From second upsert } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn can_update_device_id() { + async fn username_not_in_db_returns_none() { + let db = create_db().await; + let user = db + .get_user("does not exist") + .await + .expect("Get user query failure"); + + assert!(user.is_none()); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn authenticate_user_is_some_with_valid_password() { let db = create_db().await; - db.set_device_id("device_id") + let insert_result = db + .upsert_user(&User { + username: "myuser".to_string(), + password: crate::logic::hash_password("abc").expect("password hash error!"), + }) + .await; + + assert!(insert_result.is_ok()); + + let user = db + .authenticate_user("myuser", "abc") .await - .expect("Could not set device ID"); + .expect("User retrieval query failed"); - db.set_device_id("device_id2") + assert!(user.is_some()); + let user = user.unwrap(); + assert_eq!(user.username, "myuser"); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn authenticate_user_is_none_with_wrong_password() { + let db = create_db().await; + + let insert_result = db + .upsert_user(&User { + username: "myuser".to_string(), + password: crate::logic::hash_password("abc").expect("password hash error!"), + }) + .await; + + assert!(insert_result.is_ok()); + + let user = db + .authenticate_user("myuser", "wrong-password") .await - .expect("Could not set device ID"); + .expect("User retrieval query failed"); - let device_id = db.get_device_id().await.expect("Could not get device ID"); - - assert!(device_id.is_some()); - assert_eq!(device_id.unwrap(), "device_id2"); + assert!(user.is_none()); } } diff --git a/src/models.rs b/src/models.rs index 20f7f98..ea22071 100644 --- a/src/models.rs +++ b/src/models.rs @@ -18,3 +18,28 @@ impl User { argon2::verify_encoded(&self.password, raw_password.as_bytes()).unwrap_or(false) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_password_passes_with_correct_password() { + let user = User { + username: "myuser".to_string(), + password: crate::logic::hash_password("mypassword").expect("Password hashing error!"), + }; + + assert_eq!(user.verify_password("mypassword"), true); + } + + #[test] + fn verify_password_fails_with_wrong_password() { + let user = User { + username: "myuser".to_string(), + password: crate::logic::hash_password("mypassword").expect("Password hashing error!"), + }; + + assert_eq!(user.verify_password("wrong-password"), false); + } +}