//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2 use rand::{rngs::OsRng, RngCore}; use scrypt::{scrypt, Params}; use sea_orm::{entity::prelude::*, ActiveValue::Set, QuerySelect}; use tokio::task::spawn_blocking; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[sea_orm(table_name = "master_pass")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub user_id: u64, #[sea_orm(column_type = "Binary(BlobSize::Blob(Some(64)))")] pub salt: Vec<u8>, #[sea_orm(column_type = "Binary(BlobSize::Blob(Some(64)))")] pub password_hash: Vec<u8>, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} /// Hashes the password with Scrypt with the given salt fn hash_password(password: &[u8], salt: &[u8]) -> crate::Result<Vec<u8>> { let params = Params::new(14, Params::RECOMMENDED_R, Params::RECOMMENDED_P, 64)?; let mut password_hash = vec![0; 64]; scrypt(password, salt, ¶ms, &mut password_hash)?; Ok(password_hash) } impl ActiveModel { /// Hashes the password and creates an ActiveModel with all fields set to Set variant pub fn from_unencrypted(user_id: u64, password: &str) -> crate::Result<Self> { let mut salt = vec![0; 64]; OsRng.fill_bytes(&mut salt); let password_hash = Set(hash_password(password.as_ref(), &salt)?); Ok(Self { user_id: Set(user_id), salt: Set(salt), password_hash, }) } } impl Entity { /// Verifies the provided master password against the one from DB pub async fn verify_master_pass( user_id: u64, master_pass: String, db: &DatabaseConnection, ) -> crate::Result<Option<bool>> { let model = match Self::find_by_id(user_id).one(db).await? { Some(model) => model, None => return Ok(None), }; let salt = model.salt; let password_hash = spawn_blocking(move || hash_password(master_pass.as_bytes(), &salt)).await??; Ok(Some(password_hash == model.password_hash)) } /// Checks if the master password for the user exists pub async fn exists(user_id: u64, db: &DatabaseConnection) -> crate::Result<bool> { let id = Self::find() .select_only() .column(Column::UserId) .filter(Column::UserId.eq(user_id)) .into_tuple::<u64>() .one(db) .await?; Ok(id.is_some()) } }