Sepparated the code out into 3 more library crates: cryptography, entity and pass_manager
This commit is contained in:
		
							
								
								
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
    "rust-analyzer.linkedProjects": [
 | 
			
		||||
        "./Cargo.toml"
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -460,6 +460,22 @@ dependencies = [
 | 
			
		||||
 "typenum",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cryptography"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "arrayvec",
 | 
			
		||||
 "bitflags 2.3.1",
 | 
			
		||||
 "chacha20poly1305",
 | 
			
		||||
 "entity",
 | 
			
		||||
 "pbkdf2",
 | 
			
		||||
 "rand",
 | 
			
		||||
 "scrypt",
 | 
			
		||||
 "sea-orm",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "thiserror",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "darling"
 | 
			
		||||
version = "0.13.4"
 | 
			
		||||
@@ -566,6 +582,14 @@ dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "entity"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "futures",
 | 
			
		||||
 "sea-orm",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "env_logger"
 | 
			
		||||
version = "0.10.0"
 | 
			
		||||
@@ -1314,21 +1338,17 @@ version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "arrayvec",
 | 
			
		||||
 "bitflags 2.3.1",
 | 
			
		||||
 "chacha20poly1305",
 | 
			
		||||
 "cryptography",
 | 
			
		||||
 "dotenv",
 | 
			
		||||
 "entity",
 | 
			
		||||
 "futures",
 | 
			
		||||
 "itertools 0.10.5",
 | 
			
		||||
 "log",
 | 
			
		||||
 "migration",
 | 
			
		||||
 "pbkdf2",
 | 
			
		||||
 "pretty_env_logger",
 | 
			
		||||
 "rand",
 | 
			
		||||
 "scrypt",
 | 
			
		||||
 "sea-orm",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "teloxide",
 | 
			
		||||
 "thiserror",
 | 
			
		||||
 "tokio",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Cargo.toml
									
									
									
									
									
								
							@@ -9,26 +9,22 @@ edition = "2021"
 | 
			
		||||
strip = true
 | 
			
		||||
 | 
			
		||||
[workspace]
 | 
			
		||||
members = [".", "migration"]
 | 
			
		||||
members = [".", "migration", "entity", "cryptography"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
anyhow = "1.0.71"
 | 
			
		||||
arrayvec = "0.7.2"
 | 
			
		||||
bitflags = "2.3.1"
 | 
			
		||||
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
 | 
			
		||||
cryptography = { version = "0.1.0", path = "cryptography" }
 | 
			
		||||
dotenv = "0.15.0"
 | 
			
		||||
entity = { version = "0.1.0", path = "entity" }
 | 
			
		||||
futures = "0.3.28"
 | 
			
		||||
itertools = "0.10.5"
 | 
			
		||||
log = "0.4.17"
 | 
			
		||||
migration = { version = "0.2.0", path = "migration" }
 | 
			
		||||
pbkdf2 = "0.12.1"
 | 
			
		||||
pretty_env_logger = "0.5.0"
 | 
			
		||||
rand = { version = "0.8.5", default-features = false, features = ["std_rng"] }
 | 
			
		||||
scrypt = { version = "0.11.0", default-features = false, features = ["std"] }
 | 
			
		||||
sea-orm = { version = "0.11.3", features = ["sqlx-mysql", "runtime-tokio-rustls"] }
 | 
			
		||||
serde = "1.0.163"
 | 
			
		||||
serde_json = "1.0.96"
 | 
			
		||||
sha2 = "0.10.6"
 | 
			
		||||
teloxide = { version = "0.12.2", features = ["macros", "ctrlc_handler", "rustls", "throttle"], default-features = false }
 | 
			
		||||
thiserror = "1.0.40"
 | 
			
		||||
tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"] }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								cryptography/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								cryptography/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "cryptography"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
sha2 = "0.10.6"
 | 
			
		||||
scrypt = { version = "0.11.0", default-features = false, features = ["std"] }
 | 
			
		||||
pbkdf2 = "0.12.1"
 | 
			
		||||
thiserror = "1.0.40"
 | 
			
		||||
entity = { version = "0.1.0", path = "../entity" }
 | 
			
		||||
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
 | 
			
		||||
rand = { version = "0.8.5", default-features = false, features = ["std_rng"] }
 | 
			
		||||
sea-orm = "0.11.3"
 | 
			
		||||
bitflags = "2.3.1"
 | 
			
		||||
arrayvec = "0.7.2"
 | 
			
		||||
							
								
								
									
										88
									
								
								cryptography/src/account.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								cryptography/src/account.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
use chacha20poly1305::{aead::Aead, AeadCore, ChaCha20Poly1305, KeyInit};
 | 
			
		||||
use entity::account;
 | 
			
		||||
use pbkdf2::pbkdf2_hmac_array;
 | 
			
		||||
use rand::{rngs::OsRng, RngCore};
 | 
			
		||||
use sea_orm::ActiveValue::Set;
 | 
			
		||||
use sha2::Sha256;
 | 
			
		||||
 | 
			
		||||
struct Cipher {
 | 
			
		||||
    chacha: ChaCha20Poly1305,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Cipher {
 | 
			
		||||
    /// Creates a new cipher from a master password and the salt
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn new(password: &[u8], salt: &[u8]) -> Self {
 | 
			
		||||
        let key = pbkdf2_hmac_array::<Sha256, 32>(password, salt, 480000);
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            chacha: ChaCha20Poly1305::new(&key.into()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Encrypts the value with the current cipher. The 12 byte nonce is appended to the result
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn encrypt(&self, value: &[u8]) -> crate::Result<Vec<u8>> {
 | 
			
		||||
        let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
 | 
			
		||||
        let mut result = self.chacha.encrypt(&nonce, value)?;
 | 
			
		||||
        result.extend(nonce);
 | 
			
		||||
        Ok(result)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Decrypts the value with the current cipher. The 12 byte nonce is expected to be at the end of the value
 | 
			
		||||
    #[inline]
 | 
			
		||||
    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(Into::into)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait AccountFromUnencryptedExt {
 | 
			
		||||
    fn from_unencrypted(
 | 
			
		||||
        user_id: u64,
 | 
			
		||||
        name: String,
 | 
			
		||||
        login: &str,
 | 
			
		||||
        password: &str,
 | 
			
		||||
        master_pass: &str,
 | 
			
		||||
    ) -> crate::Result<account::ActiveModel>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AccountFromUnencryptedExt for account::ActiveModel {
 | 
			
		||||
    /// Encryptes the provided data by the master password and creates the ActiveModel with all fields set to Set variant
 | 
			
		||||
    #[inline]
 | 
			
		||||
    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_bytes(), &salt);
 | 
			
		||||
        let enc_login = Set(cipher.encrypt(login.as_bytes())?);
 | 
			
		||||
        let enc_password = Set(cipher.encrypt(password.as_bytes())?);
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            name: Set(name),
 | 
			
		||||
            user_id: Set(user_id),
 | 
			
		||||
            salt: Set(salt),
 | 
			
		||||
            enc_login,
 | 
			
		||||
            enc_password,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait DecryptAccountExt {
 | 
			
		||||
    fn decrypt(&self, master_pass: &str) -> crate::Result<(String, String)>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DecryptAccountExt for account::Model {
 | 
			
		||||
    /// Returns the decrypted login and password of the account
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn decrypt(&self, master_pass: &str) -> crate::Result<(String, String)> {
 | 
			
		||||
        let cipher = Cipher::new(master_pass.as_bytes(), &self.salt);
 | 
			
		||||
        let login = String::from_utf8(cipher.decrypt(&self.enc_login)?)?;
 | 
			
		||||
        let password = String::from_utf8(cipher.decrypt(&self.enc_password)?)?;
 | 
			
		||||
        Ok((login, password))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								cryptography/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								cryptography/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
//! Functions to encrypt the database models
 | 
			
		||||
 | 
			
		||||
pub mod account;
 | 
			
		||||
pub mod master_pass;
 | 
			
		||||
pub mod password_generation;
 | 
			
		||||
pub mod prelude;
 | 
			
		||||
 | 
			
		||||
#[derive(thiserror::Error, Debug)]
 | 
			
		||||
pub enum Error {
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    ChaChaError(#[from] chacha20poly1305::Error),
 | 
			
		||||
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    InvalidUTF8(#[from] std::string::FromUtf8Error),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Result<T> = std::result::Result<T, Error>;
 | 
			
		||||
							
								
								
									
										45
									
								
								cryptography/src/master_pass.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								cryptography/src/master_pass.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
use entity::master_pass;
 | 
			
		||||
use rand::{rngs::OsRng, RngCore};
 | 
			
		||||
use scrypt::{scrypt, Params};
 | 
			
		||||
use sea_orm::ActiveValue::Set;
 | 
			
		||||
 | 
			
		||||
/// Hashes the password with Scrypt with the given salt
 | 
			
		||||
#[inline]
 | 
			
		||||
fn hash_password(password: &[u8], salt: &[u8]) -> [u8; 64] {
 | 
			
		||||
    let params = Params::new(14, Params::RECOMMENDED_R, Params::RECOMMENDED_P, 64).unwrap();
 | 
			
		||||
    let mut password_hash = [0; 64];
 | 
			
		||||
    scrypt(password, salt, ¶ms, &mut password_hash).unwrap();
 | 
			
		||||
    password_hash
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait VerifyMasterPassExt {
 | 
			
		||||
    fn verify(&self, password: &str) -> bool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VerifyMasterPassExt for master_pass::Model {
 | 
			
		||||
    /// Checks that the given password hash matches the one of the model
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn verify(&self, password: &str) -> bool {
 | 
			
		||||
        let hashed = hash_password(password.as_bytes(), &self.salt);
 | 
			
		||||
        hashed == self.password_hash.as_slice()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait MasterPassFromUnencryptedExt {
 | 
			
		||||
    fn from_unencrypted(user_id: u64, password: &str) -> master_pass::ActiveModel;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MasterPassFromUnencryptedExt for master_pass::ActiveModel {
 | 
			
		||||
    /// Hashes the password and creates an ActiveModel with all fields set to Set variant
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn from_unencrypted(user_id: u64, password: &str) -> Self {
 | 
			
		||||
        let mut salt = vec![0; 64];
 | 
			
		||||
        OsRng.fill_bytes(&mut salt);
 | 
			
		||||
        let password_hash = Set(hash_password(password.as_bytes(), &salt).to_vec());
 | 
			
		||||
        Self {
 | 
			
		||||
            user_id: Set(user_id),
 | 
			
		||||
            salt: Set(salt),
 | 
			
		||||
            password_hash,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +1,10 @@
 | 
			
		||||
use crate::handlers::markups::deletion_markup;
 | 
			
		||||
use arrayvec::{ArrayString, ArrayVec};
 | 
			
		||||
use rand::{rngs::OsRng, seq::SliceRandom};
 | 
			
		||||
use std::{fmt::Write, str::from_utf8_unchecked};
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
 | 
			
		||||
use tokio::task::spawn_blocking;
 | 
			
		||||
use std::str::from_utf8_unchecked;
 | 
			
		||||
 | 
			
		||||
const CHARS: &[u8] = br##"!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"##;
 | 
			
		||||
 | 
			
		||||
bitflags::bitflags! {
 | 
			
		||||
    #[derive(PartialEq)]
 | 
			
		||||
    struct PasswordFlags: u8 {
 | 
			
		||||
        const LOWERCASE = 0b0001;
 | 
			
		||||
        const UPPERCASE = 0b0010;
 | 
			
		||||
@@ -18,7 +14,7 @@ bitflags::bitflags! {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Returns true if the generated master password is valid.
 | 
			
		||||
/// It checks that it has at least one lowercase, one lowercase and one punctuation char
 | 
			
		||||
/// It checks that it has at least one lowercase, one uppercase, one number and one punctuation char
 | 
			
		||||
#[inline]
 | 
			
		||||
fn check_generated_password(password: &[u8]) -> bool {
 | 
			
		||||
    let mut flags = PasswordFlags::empty();
 | 
			
		||||
@@ -32,7 +28,7 @@ fn check_generated_password(password: &[u8]) -> bool {
 | 
			
		||||
            }
 | 
			
		||||
            _ => (),
 | 
			
		||||
        }
 | 
			
		||||
        if flags == PasswordFlags::all() {
 | 
			
		||||
        if flags.is_all() {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -41,7 +37,7 @@ fn check_generated_password(password: &[u8]) -> bool {
 | 
			
		||||
 | 
			
		||||
/// Continuously generates the password until it passes the checks
 | 
			
		||||
#[inline]
 | 
			
		||||
fn generate_passwords() -> [ArrayString<32>; 10] {
 | 
			
		||||
pub fn generate_passwords() -> [ArrayString<32>; 10] {
 | 
			
		||||
    let mut passwords = ArrayVec::new_const();
 | 
			
		||||
    while !passwords.is_full() {
 | 
			
		||||
        let password: ArrayVec<u8, 32> = (0..32)
 | 
			
		||||
@@ -55,17 +51,3 @@ fn generate_passwords() -> [ArrayString<32>; 10] {
 | 
			
		||||
    }
 | 
			
		||||
    unsafe { passwords.into_inner_unchecked() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Handles /gen_password command by generating 10 copyable passwords and sending them to the user
 | 
			
		||||
pub async fn gen_password(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
 | 
			
		||||
    let mut message: ArrayString<{ 10 + 35 * 10 }> = "Passwords:".try_into().unwrap();
 | 
			
		||||
    let passwords = spawn_blocking(generate_passwords).await?;
 | 
			
		||||
    for password in passwords {
 | 
			
		||||
        write!(message, "\n`{password}`").unwrap();
 | 
			
		||||
    }
 | 
			
		||||
    bot.send_message(msg.chat.id, message.as_str())
 | 
			
		||||
        .parse_mode(ParseMode::MarkdownV2)
 | 
			
		||||
        .reply_markup(deletion_markup())
 | 
			
		||||
        .await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								cryptography/src/prelude.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								cryptography/src/prelude.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
pub use super::account::*;
 | 
			
		||||
pub use super::master_pass::*;
 | 
			
		||||
							
								
								
									
										11
									
								
								entity/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								entity/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "entity"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
futures = "0.3.28"
 | 
			
		||||
sea-orm = "0.11.3"
 | 
			
		||||
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
use chacha20poly1305::{aead::Aead, AeadCore, ChaCha20Poly1305, KeyInit};
 | 
			
		||||
use futures::{Stream, TryStreamExt};
 | 
			
		||||
use pbkdf2::pbkdf2_hmac_array;
 | 
			
		||||
use rand::{rngs::OsRng, RngCore};
 | 
			
		||||
use sea_orm::{prelude::*, ActiveValue::Set, QueryOrder, QuerySelect};
 | 
			
		||||
use sha2::Sha256;
 | 
			
		||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
 | 
			
		||||
 | 
			
		||||
use futures::Stream;
 | 
			
		||||
use sea_orm::{entity::prelude::*, QueryOrder, QuerySelect};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
 | 
			
		||||
#[sea_orm(table_name = "account")]
 | 
			
		||||
@@ -25,74 +23,6 @@ pub enum Relation {}
 | 
			
		||||
 | 
			
		||||
impl ActiveModelBehavior for ActiveModel {}
 | 
			
		||||
 | 
			
		||||
struct Cipher {
 | 
			
		||||
    chacha: ChaCha20Poly1305,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Cipher {
 | 
			
		||||
    /// Creates a new cipher from a master password and the salt
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn new(password: &[u8], salt: &[u8]) -> Self {
 | 
			
		||||
        let key = pbkdf2_hmac_array::<Sha256, 32>(password, salt, 480000);
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            chacha: ChaCha20Poly1305::new(&key.into()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Encrypts the value with the current cipher. The 12 byte nonce is appended to the result
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn encrypt(&self, value: &[u8]) -> crate::Result<Vec<u8>> {
 | 
			
		||||
        let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
 | 
			
		||||
        let mut result = self.chacha.encrypt(&nonce, value)?;
 | 
			
		||||
        result.extend(nonce);
 | 
			
		||||
        Ok(result)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Decrypts the value with the current cipher. The 12 byte nonce is expected to be at the end of the value
 | 
			
		||||
    #[inline]
 | 
			
		||||
    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(Into::into)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ActiveModel {
 | 
			
		||||
    /// Encryptes the provided data by the master password and creates the ActiveModel with all fields set to Set variant
 | 
			
		||||
    #[inline]
 | 
			
		||||
    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_bytes(), &salt);
 | 
			
		||||
        let enc_login = Set(cipher.encrypt(login.as_bytes())?);
 | 
			
		||||
        let enc_password = Set(cipher.encrypt(password.as_bytes())?);
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            name: Set(name),
 | 
			
		||||
            user_id: Set(user_id),
 | 
			
		||||
            salt: Set(salt),
 | 
			
		||||
            enc_login,
 | 
			
		||||
            enc_password,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Model {
 | 
			
		||||
    /// Returns the decrypted login and password of the account
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub fn decrypt(&self, master_pass: &str) -> crate::Result<(String, String)> {
 | 
			
		||||
        let cipher = Cipher::new(master_pass.as_bytes(), &self.salt);
 | 
			
		||||
        let login = String::from_utf8(cipher.decrypt(&self.enc_login)?)?;
 | 
			
		||||
        let password = String::from_utf8(cipher.decrypt(&self.enc_password)?)?;
 | 
			
		||||
        Ok((login, password))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Entity {
 | 
			
		||||
    /// Gets all user's account from DB
 | 
			
		||||
    #[inline]
 | 
			
		||||
@@ -104,7 +34,7 @@ impl Entity {
 | 
			
		||||
            .filter(Column::UserId.eq(user_id))
 | 
			
		||||
            .stream(db)
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(result.err_into())
 | 
			
		||||
        Ok(result)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets a list of account names of a user
 | 
			
		||||
@@ -122,7 +52,7 @@ impl Entity {
 | 
			
		||||
            select = select.order_by_asc(Column::Name);
 | 
			
		||||
        }
 | 
			
		||||
        let result = select.into_tuple().stream(db).await?;
 | 
			
		||||
        Ok(result.err_into())
 | 
			
		||||
        Ok(result)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Checks if the account exists
 | 
			
		||||
							
								
								
									
										7
									
								
								entity/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								entity/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
pub mod account;
 | 
			
		||||
pub mod master_pass;
 | 
			
		||||
pub mod prelude;
 | 
			
		||||
 | 
			
		||||
use sea_orm::DbErr;
 | 
			
		||||
 | 
			
		||||
type Result<T> = std::result::Result<T, DbErr>;
 | 
			
		||||
							
								
								
									
										45
									
								
								entity/src/master_pass.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								entity/src/master_pass.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
 | 
			
		||||
 | 
			
		||||
use sea_orm::{entity::prelude::*, QuerySelect};
 | 
			
		||||
 | 
			
		||||
#[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 {}
 | 
			
		||||
 | 
			
		||||
impl Entity {
 | 
			
		||||
    /// Gets the master password from the database
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub async fn get(user_id: u64, db: &DatabaseConnection) -> crate::Result<Option<Model>> {
 | 
			
		||||
        Self::find_by_id(user_id).one(db).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Checks if the master password for the user exists
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub async fn exists(user_id: u64, db: &DatabaseConnection) -> Result<bool, DbErr> {
 | 
			
		||||
        let id = Self::find_by_id(user_id)
 | 
			
		||||
            .select_only()
 | 
			
		||||
            .column(Column::UserId)
 | 
			
		||||
            .into_tuple::<u64>()
 | 
			
		||||
            .one(db)
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(id.is_some())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes a master password of the user from the database
 | 
			
		||||
    pub async fn remove(user_id: u64, db: &DatabaseConnection) -> Result<(), DbErr> {
 | 
			
		||||
        Self::delete_by_id(user_id).exec(db).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
 | 
			
		||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
 | 
			
		||||
 | 
			
		||||
pub use super::account::{self, Entity as Account};
 | 
			
		||||
pub use super::master_pass::{self, Entity as MasterPass};
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::handlers::markups::deletion_markup;
 | 
			
		||||
use crate::markups::deletion_markup;
 | 
			
		||||
use teloxide::{adaptors::Throttle, dispatching::DpHandlerDescription, prelude::*};
 | 
			
		||||
 | 
			
		||||
/// Deletes the message from the callback
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::NoUserInfo,
 | 
			
		||||
    handlers::{markups::deletion_markup, state::NameCheckKind, Handler, MainDialogue, State},
 | 
			
		||||
    models::DecryptedAccount,
 | 
			
		||||
    errors::NoUserInfo, markups::deletion_markup, models::DecryptedAccount, state::NameCheckKind,
 | 
			
		||||
    Handler, MainDialogue, State,
 | 
			
		||||
};
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::handlers::markups::deletion_markup;
 | 
			
		||||
use crate::markups::deletion_markup;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
 | 
			
		||||
/// Handles /cancel command when there's no active state
 | 
			
		||||
@@ -1,12 +1,10 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    entity::prelude::Account,
 | 
			
		||||
    errors::NoUserInfo,
 | 
			
		||||
    handlers::{
 | 
			
		||||
    markups::{self, deletion_markup},
 | 
			
		||||
    state::NameCheckKind,
 | 
			
		||||
    Handler, MainDialogue, State,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
 | 
			
		||||
@@ -1,9 +1,6 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    entity::prelude::*,
 | 
			
		||||
    errors::NoUserInfo,
 | 
			
		||||
    handlers::{markups::deletion_markup, Handler, MainDialogue, State},
 | 
			
		||||
};
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use crate::{errors::NoUserInfo, markups::deletion_markup, Handler, MainDialogue, State};
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use sea_orm::DatabaseConnection;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
use tokio::join;
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +20,7 @@ async fn get_master_pass(
 | 
			
		||||
        MasterPass::remove(user_id, &db),
 | 
			
		||||
    ) {
 | 
			
		||||
        (Ok(_), Ok(_)) => (),
 | 
			
		||||
        (Err(err), _) | (Ok(_), Err(err)) => return Err(err),
 | 
			
		||||
        (Err(err), _) | (Ok(_), Err(err)) => return Err(err.into()),
 | 
			
		||||
    };
 | 
			
		||||
    bot.send_message(msg.chat.id, "Everything was deleted")
 | 
			
		||||
        .reply_markup(deletion_markup())
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    entity::prelude::Account,
 | 
			
		||||
    errors::NoUserInfo,
 | 
			
		||||
    handlers::{markups::deletion_markup, Handler, MainDialogue, State},
 | 
			
		||||
    markups::deletion_markup,
 | 
			
		||||
    models::{DecryptedAccount, User},
 | 
			
		||||
    Handler, MainDialogue, State,
 | 
			
		||||
};
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use futures::TryStreamExt;
 | 
			
		||||
use sea_orm::DatabaseConnection;
 | 
			
		||||
use serde_json::to_vec_pretty;
 | 
			
		||||
@@ -26,6 +27,7 @@ async fn get_master_pass(
 | 
			
		||||
        let master_pass: Arc<str> = master_pass.into();
 | 
			
		||||
        Account::get_all(user_id, &db)
 | 
			
		||||
            .await?
 | 
			
		||||
            .err_into::<crate::Error>()
 | 
			
		||||
            .try_for_each_concurrent(None, |account| {
 | 
			
		||||
                let master_pass = Arc::clone(&master_pass);
 | 
			
		||||
                async move {
 | 
			
		||||
							
								
								
									
										20
									
								
								src/commands/gen_password.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/commands/gen_password.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
use crate::markups::deletion_markup;
 | 
			
		||||
use arrayvec::ArrayString;
 | 
			
		||||
use cryptography::password_generation::generate_passwords;
 | 
			
		||||
use std::fmt::Write;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
 | 
			
		||||
use tokio::task::spawn_blocking;
 | 
			
		||||
 | 
			
		||||
/// Handles /gen_password command by generating 10 copyable passwords and sending them to the user
 | 
			
		||||
pub async fn gen_password(bot: Throttle<Bot>, msg: Message) -> crate::Result<()> {
 | 
			
		||||
    let mut message: ArrayString<{ 10 + 35 * 10 }> = "Passwords:".try_into().unwrap();
 | 
			
		||||
    let passwords = spawn_blocking(generate_passwords).await?;
 | 
			
		||||
    for password in passwords {
 | 
			
		||||
        write!(message, "\n`{password}`").unwrap();
 | 
			
		||||
    }
 | 
			
		||||
    bot.send_message(msg.chat.id, message.as_str())
 | 
			
		||||
        .parse_mode(ParseMode::MarkdownV2)
 | 
			
		||||
        .reply_markup(deletion_markup())
 | 
			
		||||
        .await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +1,12 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    entity::prelude::Account,
 | 
			
		||||
    errors::NoUserInfo,
 | 
			
		||||
    handlers::{
 | 
			
		||||
    markups::{self, deletion_markup},
 | 
			
		||||
    state::NameCheckKind,
 | 
			
		||||
    Handler, MainDialogue, State,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use cryptography::prelude::*;
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use sea_orm::DatabaseConnection;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
 | 
			
		||||
use tokio::task::spawn_blocking;
 | 
			
		||||
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
use crate::{entity::prelude::Account, errors::NoUserInfo, handlers::markups::deletion_markup};
 | 
			
		||||
use crate::{errors::NoUserInfo, markups::deletion_markup};
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use futures::TryStreamExt;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use sea_orm::DatabaseConnection;
 | 
			
		||||
use std::fmt::Write;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
 | 
			
		||||
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::handlers::{markups::deletion_markup, Command};
 | 
			
		||||
use crate::{markups::deletion_markup, Command};
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*, utils::command::BotCommands};
 | 
			
		||||
 | 
			
		||||
/// Handles the help command by sending the passwords descryptions
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::NoUserInfo,
 | 
			
		||||
    handlers::{markups::deletion_markup, Handler, MainDialogue, State},
 | 
			
		||||
    models::User,
 | 
			
		||||
    errors::NoUserInfo, markups::deletion_markup, models::User, Handler, MainDialogue, State,
 | 
			
		||||
};
 | 
			
		||||
use futures::{future, stream::FuturesUnordered, StreamExt};
 | 
			
		||||
use itertools::Itertools;
 | 
			
		||||
@@ -1,8 +1,6 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    entity::prelude::*,
 | 
			
		||||
    errors::NoUserInfo,
 | 
			
		||||
    handlers::{markups::deletion_markup, Handler, MainDialogue, State},
 | 
			
		||||
};
 | 
			
		||||
use crate::{errors::NoUserInfo, markups::deletion_markup, Handler, MainDialogue, State};
 | 
			
		||||
use cryptography::prelude::*;
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
use tokio::task;
 | 
			
		||||
@@ -20,7 +18,7 @@ async fn get_master_pass(
 | 
			
		||||
    let model = task::spawn_blocking(move || {
 | 
			
		||||
        master_pass::ActiveModel::from_unencrypted(user_id, &master_password)
 | 
			
		||||
    })
 | 
			
		||||
    .await??;
 | 
			
		||||
    .await?;
 | 
			
		||||
    model.insert(&db).await?;
 | 
			
		||||
    bot.send_message(msg.chat.id, "Success")
 | 
			
		||||
        .reply_markup(deletion_markup())
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::handlers::markups::deletion_markup;
 | 
			
		||||
use crate::markups::deletion_markup;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
 | 
			
		||||
/// Handles the messages which weren't matched by any commands or states
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
use rand::{rngs::OsRng, RngCore};
 | 
			
		||||
use scrypt::{scrypt, Params};
 | 
			
		||||
use sea_orm::{entity::prelude::*, ActiveValue::Set, QuerySelect};
 | 
			
		||||
 | 
			
		||||
#[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
 | 
			
		||||
#[inline]
 | 
			
		||||
fn hash_password(password: &[u8], salt: &[u8]) -> crate::Result<[u8; 64]> {
 | 
			
		||||
    let params = Params::new(14, Params::RECOMMENDED_R, Params::RECOMMENDED_P, 64)?;
 | 
			
		||||
    let mut password_hash = [0; 64];
 | 
			
		||||
    scrypt(password, salt, ¶ms, &mut password_hash)?;
 | 
			
		||||
    Ok(password_hash)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Model {
 | 
			
		||||
    /// Checks that the given password hash matches the one of the model
 | 
			
		||||
    pub fn verify(&self, password: &str) -> crate::Result<bool> {
 | 
			
		||||
        let hashed = hash_password(password.as_bytes(), &self.salt)?;
 | 
			
		||||
        Ok(hashed == self.password_hash.as_slice())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ActiveModel {
 | 
			
		||||
    /// Hashes the password and creates an ActiveModel with all fields set to Set variant
 | 
			
		||||
    #[inline]
 | 
			
		||||
    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_bytes(), &salt)?.to_vec());
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            user_id: Set(user_id),
 | 
			
		||||
            salt: Set(salt),
 | 
			
		||||
            password_hash,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Entity {
 | 
			
		||||
    /// Gets the master password from the database
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub async fn get(user_id: u64, db: &DatabaseConnection) -> crate::Result<Option<Model>> {
 | 
			
		||||
        Self::find_by_id(user_id).one(db).await.map_err(Into::into)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Checks if the master password for the user exists
 | 
			
		||||
    #[inline]
 | 
			
		||||
    pub async fn exists(user_id: u64, db: &DatabaseConnection) -> crate::Result<bool> {
 | 
			
		||||
        let id = Self::find_by_id(user_id)
 | 
			
		||||
            .select_only()
 | 
			
		||||
            .column(Column::UserId)
 | 
			
		||||
            .into_tuple::<u64>()
 | 
			
		||||
            .one(db)
 | 
			
		||||
            .await?;
 | 
			
		||||
        Ok(id.is_some())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes a master password of the user from the database
 | 
			
		||||
    pub async fn remove(user_id: u64, db: &DatabaseConnection) -> crate::Result<()> {
 | 
			
		||||
        Self::delete_by_id(user_id).exec(db).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
//! Entities to work with the database
 | 
			
		||||
 | 
			
		||||
pub mod account;
 | 
			
		||||
pub mod master_pass;
 | 
			
		||||
pub mod prelude;
 | 
			
		||||
@@ -1,12 +1,16 @@
 | 
			
		||||
mod callbacks;
 | 
			
		||||
mod commands;
 | 
			
		||||
mod default;
 | 
			
		||||
mod errors;
 | 
			
		||||
mod markups;
 | 
			
		||||
mod master_password_check;
 | 
			
		||||
mod models;
 | 
			
		||||
mod state;
 | 
			
		||||
mod utils;
 | 
			
		||||
 | 
			
		||||
use anyhow::{Error, Result};
 | 
			
		||||
use commands::Command;
 | 
			
		||||
use futures::future::BoxFuture as PinnedFuture;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use state::{Handler, MainDialogue, State};
 | 
			
		||||
use teloxide::{
 | 
			
		||||
							
								
								
									
										10
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,13 +1,7 @@
 | 
			
		||||
mod entity;
 | 
			
		||||
mod errors;
 | 
			
		||||
mod handlers;
 | 
			
		||||
mod models;
 | 
			
		||||
 | 
			
		||||
use anyhow::{Error, Result};
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use dotenv::dotenv;
 | 
			
		||||
use futures::future::BoxFuture as PinnedFuture;
 | 
			
		||||
use handlers::get_dispatcher;
 | 
			
		||||
use migration::{Migrator, MigratorTrait};
 | 
			
		||||
use pass_manager::get_dispatcher;
 | 
			
		||||
use sea_orm::Database;
 | 
			
		||||
use std::env;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::entity::prelude::Account;
 | 
			
		||||
use entity::prelude::Account;
 | 
			
		||||
use futures::TryStreamExt;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, KeyboardMarkup};
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
use crate::{entity::prelude::MasterPass, errors::NoUserInfo};
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use crate::errors::NoUserInfo;
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use sea_orm::DatabaseConnection;
 | 
			
		||||
use teloxide::{adaptors::Throttle, dispatching::DpHandlerDescription, prelude::*};
 | 
			
		||||
 | 
			
		||||
use super::markups::deletion_markup;
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
//! Models to export and import the accounts
 | 
			
		||||
 | 
			
		||||
use crate::entity::prelude::*;
 | 
			
		||||
use cryptography::prelude::*;
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
@@ -36,6 +37,7 @@ impl DecryptedAccount {
 | 
			
		||||
            &self.password,
 | 
			
		||||
            master_pass,
 | 
			
		||||
        )
 | 
			
		||||
        .map_err(Into::into)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the account's fields are valid
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,4 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::HandlerUsed,
 | 
			
		||||
    handlers::{markups::deletion_markup, utils::delete_optional},
 | 
			
		||||
    PinnedFuture,
 | 
			
		||||
};
 | 
			
		||||
use crate::{errors::HandlerUsed, markups::deletion_markup, utils::delete_optional, PinnedFuture};
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
 | 
			
		||||
@@ -12,7 +8,7 @@ pub async fn generic<F>(
 | 
			
		||||
    bot: Throttle<Bot>,
 | 
			
		||||
    msg: Message,
 | 
			
		||||
    db: DatabaseConnection,
 | 
			
		||||
    dialogue: crate::handlers::MainDialogue,
 | 
			
		||||
    dialogue: crate::MainDialogue,
 | 
			
		||||
    check: F,
 | 
			
		||||
    no_text_message: impl Into<String>,
 | 
			
		||||
    next: super::PackagedHandler<String>,
 | 
			
		||||
@@ -1,13 +1,11 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    entity::prelude::Account,
 | 
			
		||||
    errors::{HandlerUsed, NoUserInfo},
 | 
			
		||||
    handlers::{
 | 
			
		||||
    markups::{account_markup, deletion_markup},
 | 
			
		||||
    utils::{delete_optional, validate_field},
 | 
			
		||||
    MainDialogue,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use sea_orm::DatabaseConnection;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
 | 
			
		||||
/// Specifies the kind of checks to be run during the account name validation
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::handlers::{utils::validate_field, MainDialogue};
 | 
			
		||||
use crate::{utils::validate_field, MainDialogue};
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
use crate::{entity::prelude::MasterPass, errors::NoUserInfo, handlers::MainDialogue};
 | 
			
		||||
use crate::{errors::NoUserInfo, MainDialogue};
 | 
			
		||||
use cryptography::prelude::*;
 | 
			
		||||
use entity::prelude::*;
 | 
			
		||||
use log::error;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use sea_orm::DatabaseConnection;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
use tokio::task::spawn_blocking;
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +20,7 @@ async fn check_master_pass(
 | 
			
		||||
    let is_valid = match model {
 | 
			
		||||
        Some(model) => {
 | 
			
		||||
            let master_pass = master_pass.to_owned();
 | 
			
		||||
            spawn_blocking(move || model.verify(&master_pass)).await??
 | 
			
		||||
            spawn_blocking(move || model.verify(&master_pass)).await?
 | 
			
		||||
        }
 | 
			
		||||
        None => {
 | 
			
		||||
            error!("User was put into the GetMasterPass state with no master password set");
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
 | 
			
		||||
use crate::handlers::{utils::validate_field, MainDialogue};
 | 
			
		||||
use crate::{utils::validate_field, MainDialogue};
 | 
			
		||||
 | 
			
		||||
/// Function to handle GetPassword state
 | 
			
		||||
pub async fn get_password(
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::HandlerUsed,
 | 
			
		||||
    handlers::{markups::deletion_markup, utils::delete_optional, MainDialogue},
 | 
			
		||||
    models::User,
 | 
			
		||||
    errors::HandlerUsed, markups::deletion_markup, models::User, utils::delete_optional,
 | 
			
		||||
    MainDialogue,
 | 
			
		||||
};
 | 
			
		||||
use futures::TryStreamExt;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use crate::handlers::MainDialogue;
 | 
			
		||||
use crate::MainDialogue;
 | 
			
		||||
use sea_orm::prelude::*;
 | 
			
		||||
use std::{future::Future, sync::Arc};
 | 
			
		||||
use teloxide::{adaptors::Throttle, prelude::*};
 | 
			
		||||
		Reference in New Issue
	
	Block a user