use crate::schema::users; use diesel; use diesel::pg::PgConnection; use diesel::prelude::*; use serde::{Deserialize, Serialize}; use argon2::{ password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Argon2, }; #[derive(Serialize, Deserialize, Queryable, AsChangeset)] #[table_name = "users"] pub struct User { pub id: i32, pub username: String, pub email: String, pub password: String, } #[derive(Serialize, Deserialize, Insertable)] #[table_name = "users"] pub struct InsertableUser { pub username: String, pub email: String, pub password: String, } impl User { pub fn create(user: InsertableUser, connection: &PgConnection) -> QueryResult { // Generate a salt let salt = SaltString::generate(&mut OsRng); // Hash the password to PHC string let password_hash = Argon2::default() .hash_password(user.password.as_bytes(), &salt) .unwrap() .to_string(); let encrypted_user = InsertableUser { password: password_hash, ..user }; // Insert the user into the database diesel::insert_into(users::table) .values(encrypted_user) .execute(connection)?; // Query the database for the encrypted user users::table.order(users::id.desc()).first(connection) } pub fn get_by_username_and_password( username: String, password: String, connection: &PgConnection, ) -> Option { // Query the database for the user let res = users::table .filter(users::username.eq(username)) .get_result::(connection); // Check if the user exists match res { Ok(user) => { let parsed_hash = PasswordHash::new(&user.password).unwrap(); // Check if the password is correct if Argon2::default() .verify_password(password.as_bytes(), &parsed_hash) .is_ok() { Some(user) } else { None } } Err(_) => None, } } pub fn get_by_email(email: String, connection: &PgConnection) -> Option { // Query the database for the user let res = users::table .filter(users::email.eq(email)) .get_result::(connection); // Check if the user exists match res { Ok(user) => Some(user), Err(_) => None, } } pub fn email_is_taken(email: String, connection: &PgConnection) -> bool { // Query the database for the user let res = users::table .filter(users::email.eq(email)) .get_result::(connection); // Check if the user exists match res { Ok(_) => true, Err(_) => false, } } pub fn username_is_taken(username: String, connection: &PgConnection) -> bool { // Query the database for the user let res = users::table .filter(users::username.eq(username)) .get_result::(connection); // Check if the user exists match res { Ok(_) => true, Err(_) => false, } } }