diff --git a/seeds.sql b/seeds.sql index 98f38f8..4673b33 100644 --- a/seeds.sql +++ b/seeds.sql @@ -1,4 +1,4 @@ -INSERT INTO "user" (name, is_cox, is_admin, is_guest) VALUES('admin', false, true, false); +INSERT INTO "user" (name, is_cox, is_admin, is_guest, pw) VALUES('admin', false, true, false, '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM'); INSERT INTO "user" (name, is_cox, is_admin, is_guest) VALUES('rower', false, false, false); INSERT INTO "user" (name, is_cox, is_admin, is_guest) VALUES('guest', false, false, true); INSERT INTO "user" (name, is_cox, is_admin, is_guest) VALUES('cox', true, false, false); diff --git a/src/main.rs b/src/main.rs index cac42fe..a9327f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use rot::{model::user::Users, rest}; +use rot::rest; use sqlx::SqlitePool; #[macro_use] @@ -10,7 +10,6 @@ async fn rocket() -> _ { //let pool = SqlitePool::connect(":memory:").await.unwrap(); let pool = SqlitePool::connect("db.sqlite").await.unwrap(); //TODO: fixme - let users = Users::new(&pool).await.unwrap(); //TODO: fixme rest::start(pool) } diff --git a/src/model/mod.rs b/src/model/mod.rs index 22d12a3..62d809a 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -1 +1,2 @@ pub mod user; +//pub mod users; diff --git a/src/model/user.rs b/src/model/user.rs index 8de5758..5f5dee6 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -1,7 +1,10 @@ use std::ops::Deref; -use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; -use base64::{engine, prelude::BASE64_STANDARD_NO_PAD}; +use argon2::{ + password_hash::{rand_core::OsRng, SaltString}, + Argon2, PasswordHash, PasswordHasher, PasswordVerifier, +}; +use base64::{engine, prelude::BASE64_STANDARD_NO_PAD, Engine}; use rocket::{ request::{self, FromRequest}, Request, @@ -18,6 +21,7 @@ pub struct User { is_guest: bool, } +#[derive(Debug)] pub enum LoginError { SqlxError(sqlx::Error), InvalidAuthenticationCombo, @@ -29,7 +33,7 @@ impl From for LoginError { } } impl User { - pub async fn find_by_name(db: &SqlitePool, name: String) -> Result { + async fn find_by_name(db: &SqlitePool, name: String) -> Result { let user: User = sqlx::query_as!( User, " @@ -48,72 +52,27 @@ WHERE name like ? pub async fn login(db: &SqlitePool, name: String, pw: String) -> Result { let user = User::find_by_name(db, name).await?; + let salt = SaltString::from_b64("dS/X5/sPEKTj4Rzs/CuvzQ").unwrap(); let argon2 = Argon2::default(); - let salt = SaltString::from_b64("CdR4i0HA9e0CM").unwrap(); //TODO: generate salt for each user let password_hash = argon2 .hash_password(&pw.as_bytes(), &salt) .unwrap() .to_string(); - //TODO: fixme - let parsed_hash = PasswordHash::new(&password_hash).unwrap(); //TODO: fixme - //TODO: If user.pw == "" -> set new pw! - match Argon2::default() - .verify_password(base64::encode(pw.as_bytes()).as_bytes(), &parsed_hash) - { - Ok(_) => Ok(user), - Err(_) => Err(LoginError::InvalidAuthenticationCombo), + if password_hash == user.pw { + return Ok(user); } - } -} -pub struct Users { - users: Vec, -} - -#[derive(Debug)] -pub enum Error {} - -//#[rocket::async_trait] -//impl<'r> FromRequest<'r> for User { -// type Error = Error; -// -// async fn from_request(req: &'r Request<'_>) -> request::Outcome { -// //TODO: https://betterprogramming.pub/building-the-rust-web-app-multiple-users-and-authentication-5ca5988ddfe4 -// } -//} - -impl Deref for Users { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.users - } -} - -impl Users { - pub async fn new(pool: &SqlitePool) -> Result { - let users: Vec = sqlx::query_as!( - User, - r#" -SELECT id, name, pw, is_cox, is_admin, is_guest -FROM user - "#, - ) - .fetch_all(pool) - .await?; - - Ok(Self { users }) + Err(LoginError::InvalidAuthenticationCombo) } } #[cfg(test)] mod test { - use super::Users; + use super::User; use sqlx::SqlitePool; - #[sqlx::test] - fn user_with_test_db() { + async fn setup() -> SqlitePool { let pool = SqlitePool::connect(":memory:").await.unwrap(); sqlx::query_file!("./migration.sql") .execute(&pool) @@ -124,7 +83,30 @@ mod test { .await .unwrap(); - let users = Users::new(&pool).await.unwrap(); - assert_eq!(users.len(), 4); + pool + } + + #[sqlx::test] + fn succ_login_with_test_db() { + let pool = setup().await; + User::login(&pool, "admin".into(), "admin".into()) + .await + .unwrap(); + } + + #[sqlx::test] + fn wrong_pw() { + let pool = setup().await; + assert!(User::login(&pool, "admin".into(), "admi".into()) + .await + .is_err()); + } + + #[sqlx::test] + fn wrong_username() { + let pool = setup().await; + assert!(User::login(&pool, "admi".into(), "admin".into()) + .await + .is_err()); } } diff --git a/src/model/users.rs b/src/model/users.rs new file mode 100644 index 0000000..cc39f84 --- /dev/null +++ b/src/model/users.rs @@ -0,0 +1,61 @@ +pub struct Users { + users: Vec, +} + +#[derive(Debug)] +pub enum Error {} + +//#[rocket::async_trait] +//impl<'r> FromRequest<'r> for User { +// type Error = Error; +// +// async fn from_request(req: &'r Request<'_>) -> request::Outcome { +// //TODO: https://betterprogramming.pub/building-the-rust-web-app-multiple-users-and-authentication-5ca5988ddfe4 +// } +//} + +impl Deref for Users { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.users + } +} + +impl Users { + pub async fn new(pool: &SqlitePool) -> Result { + let users: Vec = sqlx::query_as!( + User, + r#" +SELECT id, name, pw, is_cox, is_admin, is_guest +FROM user + "#, + ) + .fetch_all(pool) + .await?; + + Ok(Self { users }) + } +} + +#[cfg(test)] +mod test { + use super::Users; + use sqlx::SqlitePool; + + #[sqlx::test] + fn user_with_test_db() { + let pool = SqlitePool::connect(":memory:").await.unwrap(); + sqlx::query_file!("./migration.sql") + .execute(&pool) + .await + .unwrap(); + sqlx::query_file!("./seeds.sql") + .execute(&pool) + .await + .unwrap(); + + let users = Users::new(&pool).await.unwrap(); + assert_eq!(users.len(), 4); + } +} diff --git a/src/rest/auth.rs b/src/rest/auth.rs index 5d8d15c..42d059b 100644 --- a/src/rest/auth.rs +++ b/src/rest/auth.rs @@ -8,7 +8,7 @@ use rocket::{ use rocket_dyn_templates::{context, tera, Template}; use sqlx::SqlitePool; -use crate::model::user::{self, User, Users}; +use crate::model::user::{self, User}; #[get("/")] async fn index(flash: Option>) -> Template { diff --git a/src/rest/mod.rs b/src/rest/mod.rs index 416dfae..39a4a7a 100644 --- a/src/rest/mod.rs +++ b/src/rest/mod.rs @@ -2,8 +2,6 @@ use rocket::{get, routes, Build, Rocket}; use rocket_dyn_templates::{context, Template}; use sqlx::SqlitePool; -use crate::model::user::Users; - mod auth; #[get("/")]