119 lines
3.7 KiB
Rust
119 lines
3.7 KiB
Rust
use anyhow::{Error, Result};
|
|
use pbkdf2::{
|
|
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
|
Pbkdf2,
|
|
};
|
|
use sqlx::{mysql::MySqlPool, types::chrono as sqlx_chrono};
|
|
|
|
pub struct Account {
|
|
pub id: u64,
|
|
pub username: String,
|
|
pub email: String,
|
|
pub salt: String,
|
|
pub password: String,
|
|
pub joined: sqlx_chrono::NaiveDateTime,
|
|
pub verified: bool,
|
|
}
|
|
|
|
impl Account {
|
|
/// This doesn't check if an account with that name is already existing!
|
|
pub async fn new(
|
|
pool: &MySqlPool,
|
|
username: &String,
|
|
email: &String,
|
|
password: &String,
|
|
) -> Result<Self> {
|
|
let salt = SaltString::generate(&mut OsRng);
|
|
let hash = Pbkdf2
|
|
.hash_password(password.as_bytes(), &salt)
|
|
.map_err(|_| anyhow::Error::msg("Failed to hash the password"))?
|
|
.to_string();
|
|
|
|
let joined = sqlx_chrono::Utc::now().naive_utc();
|
|
|
|
sqlx::query!(
|
|
r#"INSERT INTO Accounts (username, email, salt, password, joined, verified) VALUES (?, ?, ?, ?, ?, false);"#,
|
|
username,
|
|
email,
|
|
salt.to_string(),
|
|
hash,
|
|
joined,
|
|
)
|
|
.execute(pool)
|
|
.await?;
|
|
|
|
match Account::from_username(pool, &username).await? {
|
|
Some(a) => Ok(a),
|
|
None => Err(Error::msg(
|
|
"The just created account can't be found in the database!",
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub async fn from_username(pool: &MySqlPool, username: &String) -> Result<Option<Self>> {
|
|
match sqlx::query!(r#"SELECT * FROM Accounts WHERE username = ?;"#, username)
|
|
.fetch_one(pool)
|
|
.await
|
|
{
|
|
Ok(row) => {
|
|
let account = Account {
|
|
id: row.id,
|
|
username: row.username,
|
|
email: row.email,
|
|
salt: row.salt,
|
|
password: row.password,
|
|
joined: row.joined,
|
|
verified: row.verified != 0,
|
|
};
|
|
Ok(Some(account))
|
|
}
|
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
|
Err(e) => Err(Error::new(e)),
|
|
}
|
|
}
|
|
|
|
pub async fn from_id(pool: &MySqlPool, id: u64) -> Result<Option<Self>> {
|
|
match sqlx::query!(r#"SELECT * FROM Accounts WHERE id = ?;"#, id)
|
|
.fetch_one(pool)
|
|
.await
|
|
{
|
|
Ok(row) => {
|
|
let account = Account {
|
|
id: row.id,
|
|
username: row.username,
|
|
email: row.email,
|
|
salt: row.salt,
|
|
password: row.password,
|
|
joined: row.joined,
|
|
verified: row.verified != 0,
|
|
};
|
|
Ok(Some(account))
|
|
}
|
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
|
Err(e) => Err(Error::new(e)),
|
|
}
|
|
}
|
|
|
|
pub async fn from_email(pool: &MySqlPool, email: &String) -> Result<Option<Self>> {
|
|
match sqlx::query!(r#"SELECT * FROM Accounts WHERE email = ?;"#, email)
|
|
.fetch_one(pool)
|
|
.await
|
|
{
|
|
Ok(row) => {
|
|
let account = Account {
|
|
id: row.id,
|
|
username: row.username,
|
|
email: row.email,
|
|
salt: row.salt,
|
|
password: row.password,
|
|
joined: row.joined,
|
|
verified: row.verified != 0,
|
|
};
|
|
Ok(Some(account))
|
|
}
|
|
Err(sqlx::Error::RowNotFound) => Ok(None),
|
|
Err(e) => Err(Error::new(e)),
|
|
}
|
|
}
|
|
}
|