2021-05-22 14:52:32 +00:00
|
|
|
use super::Database;
|
|
|
|
use crate::db::{errors::DataError, Users};
|
|
|
|
use crate::error::BotError;
|
|
|
|
use crate::models::User;
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
impl Users for Database {
|
|
|
|
async fn upsert_user(&self, user: &User) -> Result<(), DataError> {
|
|
|
|
sqlx::query(
|
|
|
|
r#"INSERT INTO accounts (user_id, password) VALUES (?, ?)
|
|
|
|
ON CONFLICT(user_id) DO UPDATE SET password = ?"#,
|
|
|
|
)
|
|
|
|
.bind(&user.username)
|
|
|
|
.bind(&user.password)
|
|
|
|
.bind(&user.password)
|
|
|
|
.execute(&self.conn)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-05-22 23:12:17 +00:00
|
|
|
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(())
|
|
|
|
}
|
|
|
|
|
2021-05-22 14:52:32 +00:00
|
|
|
async fn get_user(&self, username: &str) -> Result<Option<User>, DataError> {
|
2021-05-25 15:05:35 +00:00
|
|
|
let user_row = sqlx::query_as!(
|
|
|
|
User,
|
|
|
|
r#"SELECT
|
|
|
|
a.user_id as "username", a.password,
|
|
|
|
s.active_room as "active_room: _",
|
|
|
|
s.account_status as "account_status: _"
|
|
|
|
FROM accounts a
|
|
|
|
JOIN user_state s on a.user_id = s.user_id
|
|
|
|
WHERE a.user_id = ?"#,
|
2021-05-22 14:52:32 +00:00
|
|
|
username
|
|
|
|
)
|
|
|
|
.fetch_optional(&self.conn)
|
|
|
|
.await?;
|
|
|
|
|
2021-05-25 15:05:35 +00:00
|
|
|
Ok(user_row)
|
2021-05-22 14:52:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn authenticate_user(
|
|
|
|
&self,
|
|
|
|
username: &str,
|
|
|
|
raw_password: &str,
|
|
|
|
) -> Result<Option<User>, BotError> {
|
|
|
|
let user = self.get_user(username).await?;
|
|
|
|
Ok(user.filter(|u| u.verify_password(raw_password)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-05-22 22:48:47 +00:00
|
|
|
use super::*;
|
2021-05-22 14:52:32 +00:00
|
|
|
use crate::db::sqlite::Database;
|
2021-05-22 22:48:47 +00:00
|
|
|
use crate::db::Users;
|
2021-05-22 14:52:32 +00:00
|
|
|
|
|
|
|
async fn create_db() -> Database {
|
|
|
|
let db_path = tempfile::NamedTempFile::new_in(".").unwrap();
|
|
|
|
crate::db::sqlite::migrator::migrate(db_path.path().to_str().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
Database::new(db_path.path().to_str().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
2021-05-22 22:48:47 +00:00
|
|
|
async fn create_and_get_user_test() {
|
2021-05-22 14:52:32 +00:00
|
|
|
let db = create_db().await;
|
|
|
|
|
2021-05-22 22:48:47 +00:00
|
|
|
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")
|
2021-05-22 14:52:32 +00:00
|
|
|
.await
|
2021-05-22 22:48:47 +00:00
|
|
|
.expect("User retrieval query failed");
|
|
|
|
|
|
|
|
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 can_update_user() {
|
|
|
|
let db = create_db().await;
|
|
|
|
|
|
|
|
let insert_result1 = db
|
|
|
|
.upsert_user(&User {
|
|
|
|
username: "myuser".to_string(),
|
|
|
|
password: "abc".to_string(),
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
|
|
|
|
assert!(insert_result1.is_ok());
|
2021-05-22 14:52:32 +00:00
|
|
|
|
2021-05-22 22:48:47 +00:00
|
|
|
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");
|
2021-05-22 14:52:32 +00:00
|
|
|
|
2021-05-22 22:48:47 +00:00
|
|
|
assert!(user.is_some());
|
|
|
|
let user = user.unwrap();
|
|
|
|
assert_eq!(user.username, "myuser");
|
|
|
|
assert_eq!(user.password, "123"); //From second upsert
|
2021-05-22 14:52:32 +00:00
|
|
|
}
|
|
|
|
|
2021-05-22 23:12:17 +00:00
|
|
|
#[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());
|
|
|
|
}
|
|
|
|
|
2021-05-22 14:52:32 +00:00
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
2021-05-22 22:48:47 +00:00
|
|
|
async fn username_not_in_db_returns_none() {
|
2021-05-22 14:52:32 +00:00
|
|
|
let db = create_db().await;
|
2021-05-22 22:48:47 +00:00
|
|
|
let user = db
|
|
|
|
.get_user("does not exist")
|
|
|
|
.await
|
|
|
|
.expect("Get user query failure");
|
|
|
|
|
|
|
|
assert!(user.is_none());
|
2021-05-22 14:52:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
2021-05-22 22:48:47 +00:00
|
|
|
async fn authenticate_user_is_some_with_valid_password() {
|
2021-05-22 14:52:32 +00:00
|
|
|
let db = create_db().await;
|
|
|
|
|
2021-05-22 22:48:47 +00:00
|
|
|
let insert_result = db
|
|
|
|
.upsert_user(&User {
|
|
|
|
username: "myuser".to_string(),
|
|
|
|
password: crate::logic::hash_password("abc").expect("password hash error!"),
|
|
|
|
})
|
|
|
|
.await;
|
2021-05-22 14:52:32 +00:00
|
|
|
|
2021-05-22 22:48:47 +00:00
|
|
|
assert!(insert_result.is_ok());
|
|
|
|
|
|
|
|
let user = db
|
|
|
|
.authenticate_user("myuser", "abc")
|
2021-05-22 14:52:32 +00:00
|
|
|
.await
|
2021-05-22 22:48:47 +00:00
|
|
|
.expect("User retrieval query failed");
|
|
|
|
|
|
|
|
assert!(user.is_some());
|
|
|
|
let user = user.unwrap();
|
|
|
|
assert_eq!(user.username, "myuser");
|
|
|
|
}
|
2021-05-22 14:52:32 +00:00
|
|
|
|
2021-05-22 22:48:47 +00:00
|
|
|
#[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("User retrieval query failed");
|
2021-05-22 14:52:32 +00:00
|
|
|
|
2021-05-22 22:48:47 +00:00
|
|
|
assert!(user.is_none());
|
2021-05-22 14:52:32 +00:00
|
|
|
}
|
|
|
|
}
|