2023-04-23 17:54:16 +00:00
|
|
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
|
|
|
|
|
2023-04-27 13:25:23 +00:00
|
|
|
use chacha20poly1305::{aead::Aead, AeadCore, ChaCha20Poly1305, KeyInit};
|
2023-05-04 17:51:36 +00:00
|
|
|
use futures::{Stream, TryStreamExt};
|
2023-04-27 13:25:23 +00:00
|
|
|
use pbkdf2::pbkdf2_hmac_array;
|
|
|
|
use rand::{rngs::OsRng, RngCore};
|
2023-05-05 13:45:56 +00:00
|
|
|
use sea_orm::{prelude::*, ActiveValue::Set, QueryOrder, QuerySelect};
|
2023-04-27 13:25:23 +00:00
|
|
|
use sha2::Sha256;
|
2023-04-23 17:54:16 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
|
|
|
#[sea_orm(table_name = "account")]
|
|
|
|
pub struct Model {
|
|
|
|
#[sea_orm(primary_key, auto_increment = false)]
|
2023-04-24 17:48:34 +00:00
|
|
|
pub user_id: u64,
|
2023-04-23 17:54:16 +00:00
|
|
|
#[sea_orm(primary_key, auto_increment = false)]
|
|
|
|
pub name: String,
|
|
|
|
#[sea_orm(column_type = "Binary(BlobSize::Blob(Some(64)))")]
|
|
|
|
pub salt: Vec<u8>,
|
|
|
|
#[sea_orm(column_type = "VarBinary(256)")]
|
|
|
|
pub enc_login: Vec<u8>,
|
|
|
|
#[sea_orm(column_type = "VarBinary(256)")]
|
|
|
|
pub enc_password: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
|
|
|
pub enum Relation {}
|
|
|
|
|
|
|
|
impl ActiveModelBehavior for ActiveModel {}
|
2023-04-27 13:25:23 +00:00
|
|
|
|
|
|
|
struct Cipher {
|
|
|
|
chacha: ChaCha20Poly1305,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Cipher {
|
|
|
|
fn new(password: &[u8], salt: &[u8]) -> Self {
|
|
|
|
let key = pbkdf2_hmac_array::<Sha256, 32>(password, salt, 480000);
|
|
|
|
|
|
|
|
Self {
|
|
|
|
chacha: ChaCha20Poly1305::new(&key.into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn encrypt(&self, value: &[u8]) -> crate::Result<Vec<u8>> {
|
|
|
|
let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
|
2023-05-06 16:35:11 +00:00
|
|
|
let mut result = self.chacha.encrypt(&nonce, value)?;
|
2023-04-27 13:25:23 +00:00
|
|
|
result.extend(nonce);
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn decrypt(&self, value: &[u8]) -> crate::Result<Vec<u8>> {
|
|
|
|
let (data, nonce) = value.split_at(value.len() - 12);
|
|
|
|
self.chacha
|
|
|
|
.decrypt(nonce.into(), data)
|
|
|
|
.map_err(|err| err.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ActiveModel {
|
|
|
|
pub fn from_unencrypted(
|
|
|
|
user_id: u64,
|
|
|
|
name: String,
|
|
|
|
login: &str,
|
|
|
|
password: &str,
|
|
|
|
master_pass: &str,
|
|
|
|
) -> crate::Result<Self> {
|
|
|
|
let mut salt = vec![0; 64];
|
|
|
|
OsRng.fill_bytes(&mut salt);
|
|
|
|
let cipher = Cipher::new(master_pass.as_ref(), &salt);
|
|
|
|
let enc_login = Set(cipher.encrypt(login.as_ref())?);
|
|
|
|
let enc_password = Set(cipher.encrypt(password.as_ref())?);
|
|
|
|
Ok(Self {
|
|
|
|
name: Set(name),
|
|
|
|
user_id: Set(user_id),
|
|
|
|
salt: Set(salt),
|
|
|
|
enc_login,
|
|
|
|
enc_password,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Model {
|
|
|
|
pub fn decrypt(&self, master_pass: &str) -> crate::Result<(String, String)> {
|
|
|
|
let cipher = Cipher::new(master_pass.as_ref(), self.salt.as_ref());
|
|
|
|
let login = String::from_utf8(cipher.decrypt(self.enc_login.as_ref())?)?;
|
|
|
|
let password = String::from_utf8(cipher.decrypt(self.enc_password.as_ref())?)?;
|
|
|
|
Ok((login, password))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Entity {
|
2023-05-05 13:45:56 +00:00
|
|
|
pub async fn get_all(
|
|
|
|
user_id: u64,
|
|
|
|
db: &DatabaseConnection,
|
|
|
|
) -> crate::Result<impl Stream<Item = crate::Result<Model>> + '_> {
|
|
|
|
let result = Self::find()
|
|
|
|
.filter(Column::UserId.eq(user_id))
|
|
|
|
.stream(db)
|
|
|
|
.await?;
|
2023-05-06 16:21:40 +00:00
|
|
|
Ok(result.err_into())
|
2023-05-05 13:45:56 +00:00
|
|
|
}
|
|
|
|
|
2023-04-27 13:25:23 +00:00
|
|
|
/// Gets a list of account names of a user
|
2023-05-04 15:27:19 +00:00
|
|
|
pub async fn get_names(
|
|
|
|
user_id: u64,
|
|
|
|
db: &DatabaseConnection,
|
2023-05-05 13:45:56 +00:00
|
|
|
ordered: bool,
|
2023-05-04 15:27:19 +00:00
|
|
|
) -> crate::Result<impl Stream<Item = crate::Result<String>> + '_> {
|
2023-05-05 13:45:56 +00:00
|
|
|
let mut select = Self::find()
|
2023-04-27 13:25:23 +00:00
|
|
|
.select_only()
|
2023-04-27 16:38:34 +00:00
|
|
|
.column(Column::Name)
|
2023-05-05 13:45:56 +00:00
|
|
|
.filter(Column::UserId.eq(user_id));
|
|
|
|
if ordered {
|
|
|
|
select = select.order_by_asc(Column::Name);
|
|
|
|
}
|
|
|
|
let result = select.into_tuple().stream(db).await?;
|
2023-05-06 16:21:40 +00:00
|
|
|
Ok(result.err_into())
|
2023-04-27 13:25:23 +00:00
|
|
|
}
|
2023-05-05 13:45:56 +00:00
|
|
|
|
|
|
|
pub async fn exists(
|
|
|
|
user_id: u64,
|
|
|
|
account_name: impl Into<String>,
|
|
|
|
db: &DatabaseConnection,
|
|
|
|
) -> crate::Result<bool> {
|
|
|
|
let result = Self::find_by_id((user_id, account_name.into()))
|
|
|
|
.select_only()
|
|
|
|
.column(Column::UserId)
|
|
|
|
.into_tuple::<u64>()
|
|
|
|
.one(db)
|
|
|
|
.await?;
|
|
|
|
Ok(result.is_some())
|
|
|
|
}
|
2023-04-27 13:25:23 +00:00
|
|
|
}
|