yggdrasil/src/util/structs/account.rs

145 lines
4.5 KiB
Rust

/*
* Yggdrasil: Minecraft authentication server
* Copyright (C) 2023 0xf8.dev@proton.me
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result;
use log::debug;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use structs::profile::{Profile, ProfileRaw};
use crate::*;
// TODO: 2FA
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Account {
pub id: i64,
pub email: String,
pub password_hash: String,
pub language: String,
pub country: String,
pub selected_profile: Option<Profile>,
}
impl Account {
pub async fn from_id(db: &Database, id: i64) -> Option<Self> {
let record = sqlx::query_as!(AccountRaw, "SELECT * FROM accounts WHERE id = $1", id)
.fetch_one(&db.pool)
.await;
match record {
Ok(r) => Some(r.complete(db).await),
Err(e) => { debug!("{e}"); None },
}
}
pub async fn from_email(db: &Database, email: String) -> Option<Self> {
let record = sqlx::query_as!(AccountRaw, "SELECT * FROM accounts WHERE email = $1", email)
.fetch_one(&db.pool)
.await;
match record {
Ok(r) => Some(r.complete(db).await),
Err(e) => { debug!("{e}"); None },
}
}
pub async fn get_all_profiles(&self, db: &Database) -> Option<Vec<Profile>> {
let record = sqlx::query_as!(ProfileRaw, "SELECT * FROM profiles WHERE owner = $1", self.id).fetch_all(&db.pool).await;
match record {
Ok(r) => {
let mut collection = vec![];
for re in r {
collection.push(re.complete(db).await)
}
Some(collection)
} // oh boy
Err(e) => { debug!("{e}"); None },
}
}
pub async fn set_selected_profile(&self, db: &Database, profile: &Profile) -> Result<()> {
sqlx::query!("UPDATE accounts SET selected_profile = $1 WHERE id = $2", profile.id, self.id)
.execute(&db.pool)
.await?;
Ok(())
}
pub async fn new(db: &Database, email: String, language: String, country: String, password: String) -> Result<Account> {
let password_hash = bcrypt::hash(password, 12)?;
let r = sqlx::query!("INSERT INTO accounts(email, language, country, password_hash) VALUES ($1, $2, $3, $4) RETURNING (id)", email, language, country, password_hash)
.fetch_one(&db.pool)
.await?;
Ok(Account {
id: r.id,
email,
password_hash,
language,
country,
selected_profile: None,
})
}
pub async fn del(db: &Database, id: i64) -> Result<String> {
let r = sqlx::query!("DELETE FROM accounts WHERE id = $1 RETURNING (email)", id)
.fetch_one(&db.pool)
.await?;
Ok(r.email)
}
pub fn to_user(&self) -> Value {
json!({
"id": self.id,
"username": self.email,
"properties": [
{
"name": "preferredLanguage",
"value": self.language
},
{
"name": "registrationCountry",
"value": self.country
}
]
})
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct AccountRaw {
pub id: i64,
pub email: String,
pub password_hash: String,
pub language: String,
pub country: String,
pub selected_profile: Option<i64>
}
impl AccountRaw {
pub async fn complete(self, db: &Database) -> Account {
Account {
id: self.id,
email: self.email,
password_hash: self.password_hash,
language: self.language,
country: self.country,
selected_profile: match self.selected_profile {
None => None,
Some(id) => Profile::from_id(db, id).await
},
}
}
}