forked from Ruderverein-Donau-Linz/rowt
		
	craete login tests
This commit is contained in:
		@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,2 @@
 | 
			
		||||
pub mod user;
 | 
			
		||||
//pub mod users;
 | 
			
		||||
 
 | 
			
		||||
@@ -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<sqlx::Error> for LoginError {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl User {
 | 
			
		||||
    pub async fn find_by_name(db: &SqlitePool, name: String) -> Result<Self, sqlx::Error> {
 | 
			
		||||
    async fn find_by_name(db: &SqlitePool, name: String) -> Result<Self, sqlx::Error> {
 | 
			
		||||
        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<Self, LoginError> {
 | 
			
		||||
        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<User>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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<Self, Self::Error> {
 | 
			
		||||
//        //TODO: https://betterprogramming.pub/building-the-rust-web-app-multiple-users-and-authentication-5ca5988ddfe4
 | 
			
		||||
//    }
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
impl Deref for Users {
 | 
			
		||||
    type Target = Vec<User>;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        &self.users
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Users {
 | 
			
		||||
    pub async fn new(pool: &SqlitePool) -> Result<Self, sqlx::Error> {
 | 
			
		||||
        let users: Vec<User> = 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());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								src/model/users.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/model/users.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
pub struct Users {
 | 
			
		||||
    users: Vec<User>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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<Self, Self::Error> {
 | 
			
		||||
//        //TODO: https://betterprogramming.pub/building-the-rust-web-app-multiple-users-and-authentication-5ca5988ddfe4
 | 
			
		||||
//    }
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
impl Deref for Users {
 | 
			
		||||
    type Target = Vec<User>;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        &self.users
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Users {
 | 
			
		||||
    pub async fn new(pool: &SqlitePool) -> Result<Self, sqlx::Error> {
 | 
			
		||||
        let users: Vec<User> = 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<FlashMessage<'_>>) -> Template {
 | 
			
		||||
 
 | 
			
		||||
@@ -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("/")]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user