Tests for secure commands and user DB API.
This commit is contained in:
parent
926dae57fb
commit
8c2a90e86b
|
@ -168,6 +168,7 @@ fn log_command(cmd: &(impl Command + ?Sized), ctx: &Context, result: &ExecutionR
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use management::RegisterCommand;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
macro_rules! dummy_room {
|
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]
|
#[test]
|
||||||
fn command_result_extractor_creates_bubble() {
|
fn command_result_extractor_creates_bubble() {
|
||||||
let result = Execution::success("test".to_string());
|
let result = Execution::success("test".to_string());
|
||||||
|
@ -205,6 +300,7 @@ mod tests {
|
||||||
username: "myusername",
|
username: "myusername",
|
||||||
message_body: "!notacommand",
|
message_body: "!notacommand",
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = execute_command(&ctx).await;
|
let result = execute_command(&ctx).await;
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,9 @@ impl Users for Database {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
use crate::db::sqlite::Database;
|
use crate::db::sqlite::Database;
|
||||||
use crate::db::DbState;
|
use crate::db::Users;
|
||||||
|
|
||||||
async fn create_db() -> Database {
|
async fn create_db() -> Database {
|
||||||
let db_path = tempfile::NamedTempFile::new_in(".").unwrap();
|
let db_path = tempfile::NamedTempFile::new_in(".").unwrap();
|
||||||
|
@ -62,41 +63,114 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
#[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;
|
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
|
.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!(user.is_some());
|
||||||
|
let user = user.unwrap();
|
||||||
assert!(device_id.is_some());
|
assert_eq!(user.username, "myuser");
|
||||||
assert_eq!(device_id.unwrap(), "device_id");
|
assert_eq!(user.password, "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
#[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 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)]
|
#[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;
|
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
|
.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
|
.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!(user.is_none());
|
||||||
|
|
||||||
assert!(device_id.is_some());
|
|
||||||
assert_eq!(device_id.unwrap(), "device_id2");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,3 +18,28 @@ impl User {
|
||||||
argon2::verify_encoded(&self.password, raw_password.as_bytes()).unwrap_or(false)
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue