Files
gamenight/gamenight-database/src/user.rs

149 lines
3.7 KiB
Rust

use crate::DbConnection;
use argon2::Argon2;
use argon2::PasswordHash;
use argon2::PasswordVerifier;
use argon2::password_hash::PasswordHasher;
use argon2::password_hash::Salt;
use argon2::password_hash::SaltString;
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::rand_core::RngCore;
use diesel::Connection;
use diesel::{ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl};
use diesel_derive_enum::DbEnum;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
pub use super::error::DatabaseError;
use super::schema::{client, pwd};
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[diesel(table_name = pwd)]
struct Pwd {
user_id: Uuid,
password: String,
}
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
#[diesel(table_name = client)]
pub struct User {
pub id: Uuid,
pub username: String,
pub email: String,
pub role: Role,
}
#[derive(DbEnum, Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
#[ExistingTypePath = "crate::schema::sql_types::Role"]
pub enum Role {
Admin,
User,
}
pub struct LoginUser {
pub username: String,
pub password: String,
}
#[derive(Serialize, Deserialize)]
pub struct LoginResult {
pub result: bool,
pub user: Option<User>,
}
#[derive(Serialize, Deserialize)]
pub struct RegisterResult {
pub result: bool,
}
#[derive(Serialize, Deserialize)]
pub struct Register {
pub username: String,
pub email: String,
pub password: String,
}
pub fn login(conn: &mut DbConnection, user: LoginUser) -> Result<Option<User>, DatabaseError> {
let id: Uuid = client::table
.filter(client::username.eq(&user.username))
.or_filter(client::email.eq(&user.username))
.select(client::id)
.first(conn)?;
let pwd: String = pwd::table
.filter(pwd::user_id.eq(id))
.select(pwd::password)
.first(conn)?;
let parsed_hash = PasswordHash::new(&pwd)?;
if Argon2::default()
.verify_password(user.password.as_bytes(), &parsed_hash)
.is_ok()
{
Ok(Some(client::table.find(id).first(conn)?))
} else {
Ok(None)
}
}
pub fn get_user(conn: &mut DbConnection, id: Uuid) -> Result<User, DatabaseError> {
Ok(client::table.find(id).first(conn)?)
}
pub fn register(conn: &mut DbConnection, register: Register) -> Result<(), DatabaseError> {
let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
OsRng.try_fill_bytes(&mut bytes).unwrap();
let salt = SaltString::encode_b64(&bytes).unwrap();
let argon2 = Argon2::default();
let password_hash = argon2
.hash_password(register.password.as_bytes(), &salt)?
.to_string();
conn.transaction(|c| {
let id = Uuid::new_v4();
diesel::insert_into(client::table)
.values(User {
id,
username: register.username,
email: register.email,
role: Role::User,
})
.execute(c)?;
diesel::insert_into(pwd::table)
.values(Pwd {
user_id: id,
password: password_hash,
})
.execute(c)?;
Ok(())
})
}
pub fn count_users_with_username(
conn: &mut DbConnection,
username: &String,
) -> Result<i64, DatabaseError> {
Ok(client::table
.count()
.filter(client::username.eq(username))
.get_result::<i64>(conn)?)
}
pub fn count_users_with_email(
conn: &mut DbConnection,
email: &String,
) -> Result<i64, DatabaseError> {
Ok(client::table
.count()
.filter(client::email.eq(email))
.get_result::<i64>(conn)?)
}
pub fn get_users(conn: &mut DbConnection) -> Result<Vec<User>, DatabaseError> {
Ok(client::table.load(conn)?)
}