Sepparated the code out into 3 more library crates: cryptography, entity and pass_manager
This commit is contained in:
parent
c40ffafd69
commit
cda07b4d84
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",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cryptography"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bitflags 2.3.1",
|
||||||
|
"chacha20poly1305",
|
||||||
|
"entity",
|
||||||
|
"pbkdf2",
|
||||||
|
"rand",
|
||||||
|
"scrypt",
|
||||||
|
"sea-orm",
|
||||||
|
"sha2",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.13.4"
|
version = "0.13.4"
|
||||||
@ -566,6 +582,14 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "entity"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"sea-orm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@ -1314,21 +1338,17 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bitflags 2.3.1",
|
"cryptography",
|
||||||
"chacha20poly1305",
|
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
"entity",
|
||||||
"futures",
|
"futures",
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
"log",
|
"log",
|
||||||
"migration",
|
"migration",
|
||||||
"pbkdf2",
|
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand",
|
|
||||||
"scrypt",
|
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
|
||||||
"teloxide",
|
"teloxide",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -9,26 +9,22 @@ edition = "2021"
|
|||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [".", "migration"]
|
members = [".", "migration", "entity", "cryptography"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
arrayvec = "0.7.2"
|
arrayvec = "0.7.2"
|
||||||
bitflags = "2.3.1"
|
cryptography = { version = "0.1.0", path = "cryptography" }
|
||||||
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
|
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
entity = { version = "0.1.0", path = "entity" }
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
migration = { version = "0.2.0", path = "migration" }
|
migration = { version = "0.2.0", path = "migration" }
|
||||||
pbkdf2 = "0.12.1"
|
|
||||||
pretty_env_logger = "0.5.0"
|
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"] }
|
sea-orm = { version = "0.11.3", features = ["sqlx-mysql", "runtime-tokio-rustls"] }
|
||||||
serde = "1.0.163"
|
serde = "1.0.163"
|
||||||
serde_json = "1.0.96"
|
serde_json = "1.0.96"
|
||||||
sha2 = "0.10.6"
|
|
||||||
teloxide = { version = "0.12.2", features = ["macros", "ctrlc_handler", "rustls", "throttle"], default-features = false }
|
teloxide = { version = "0.12.2", features = ["macros", "ctrlc_handler", "rustls", "throttle"], default-features = false }
|
||||||
thiserror = "1.0.40"
|
thiserror = "1.0.40"
|
||||||
tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"] }
|
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 arrayvec::{ArrayString, ArrayVec};
|
||||||
use rand::{rngs::OsRng, seq::SliceRandom};
|
use rand::{rngs::OsRng, seq::SliceRandom};
|
||||||
use std::{fmt::Write, str::from_utf8_unchecked};
|
use std::str::from_utf8_unchecked;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
|
|
||||||
use tokio::task::spawn_blocking;
|
|
||||||
|
|
||||||
const CHARS: &[u8] = br##"!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"##;
|
const CHARS: &[u8] = br##"!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}~"##;
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
#[derive(PartialEq)]
|
|
||||||
struct PasswordFlags: u8 {
|
struct PasswordFlags: u8 {
|
||||||
const LOWERCASE = 0b0001;
|
const LOWERCASE = 0b0001;
|
||||||
const UPPERCASE = 0b0010;
|
const UPPERCASE = 0b0010;
|
||||||
@ -18,7 +14,7 @@ bitflags::bitflags! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the generated master password is valid.
|
/// 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]
|
#[inline]
|
||||||
fn check_generated_password(password: &[u8]) -> bool {
|
fn check_generated_password(password: &[u8]) -> bool {
|
||||||
let mut flags = PasswordFlags::empty();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +37,7 @@ fn check_generated_password(password: &[u8]) -> bool {
|
|||||||
|
|
||||||
/// Continuously generates the password until it passes the checks
|
/// Continuously generates the password until it passes the checks
|
||||||
#[inline]
|
#[inline]
|
||||||
fn generate_passwords() -> [ArrayString<32>; 10] {
|
pub fn generate_passwords() -> [ArrayString<32>; 10] {
|
||||||
let mut passwords = ArrayVec::new_const();
|
let mut passwords = ArrayVec::new_const();
|
||||||
while !passwords.is_full() {
|
while !passwords.is_full() {
|
||||||
let password: ArrayVec<u8, 32> = (0..32)
|
let password: ArrayVec<u8, 32> = (0..32)
|
||||||
@ -55,17 +51,3 @@ fn generate_passwords() -> [ArrayString<32>; 10] {
|
|||||||
}
|
}
|
||||||
unsafe { passwords.into_inner_unchecked() }
|
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};
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
|
||||||
use futures::{Stream, TryStreamExt};
|
|
||||||
use pbkdf2::pbkdf2_hmac_array;
|
use futures::Stream;
|
||||||
use rand::{rngs::OsRng, RngCore};
|
use sea_orm::{entity::prelude::*, QueryOrder, QuerySelect};
|
||||||
use sea_orm::{prelude::*, ActiveValue::Set, QueryOrder, QuerySelect};
|
|
||||||
use sha2::Sha256;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
#[sea_orm(table_name = "account")]
|
#[sea_orm(table_name = "account")]
|
||||||
@ -25,74 +23,6 @@ pub enum Relation {}
|
|||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
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 {
|
impl Entity {
|
||||||
/// Gets all user's account from DB
|
/// Gets all user's account from DB
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -104,7 +34,7 @@ impl Entity {
|
|||||||
.filter(Column::UserId.eq(user_id))
|
.filter(Column::UserId.eq(user_id))
|
||||||
.stream(db)
|
.stream(db)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(result.err_into())
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a list of account names of a user
|
/// Gets a list of account names of a user
|
||||||
@ -122,7 +52,7 @@ impl Entity {
|
|||||||
select = select.order_by_asc(Column::Name);
|
select = select.order_by_asc(Column::Name);
|
||||||
}
|
}
|
||||||
let result = select.into_tuple().stream(db).await?;
|
let result = select.into_tuple().stream(db).await?;
|
||||||
Ok(result.err_into())
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the account exists
|
/// 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::account::{self, Entity as Account};
|
||||||
pub use super::master_pass::{self, Entity as MasterPass};
|
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::*};
|
use teloxide::{adaptors::Throttle, dispatching::DpHandlerDescription, prelude::*};
|
||||||
|
|
||||||
/// Deletes the message from the callback
|
/// Deletes the message from the callback
|
@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::NoUserInfo,
|
errors::NoUserInfo, markups::deletion_markup, models::DecryptedAccount, state::NameCheckKind,
|
||||||
handlers::{markups::deletion_markup, state::NameCheckKind, Handler, MainDialogue, State},
|
Handler, MainDialogue, State,
|
||||||
models::DecryptedAccount,
|
|
||||||
};
|
};
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, 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::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
/// Handles /cancel command when there's no active state
|
/// Handles /cancel command when there's no active state
|
@ -1,12 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
entity::prelude::Account,
|
|
||||||
errors::NoUserInfo,
|
errors::NoUserInfo,
|
||||||
handlers::{
|
markups::{self, deletion_markup},
|
||||||
markups::{self, deletion_markup},
|
state::NameCheckKind,
|
||||||
state::NameCheckKind,
|
Handler, MainDialogue, State,
|
||||||
Handler, MainDialogue, State,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
use entity::prelude::*;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
@ -1,9 +1,6 @@
|
|||||||
use crate::{
|
use crate::{errors::NoUserInfo, markups::deletion_markup, Handler, MainDialogue, State};
|
||||||
entity::prelude::*,
|
use entity::prelude::*;
|
||||||
errors::NoUserInfo,
|
use sea_orm::DatabaseConnection;
|
||||||
handlers::{markups::deletion_markup, Handler, MainDialogue, State},
|
|
||||||
};
|
|
||||||
use sea_orm::prelude::*;
|
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
|
|
||||||
@ -23,7 +20,7 @@ async fn get_master_pass(
|
|||||||
MasterPass::remove(user_id, &db),
|
MasterPass::remove(user_id, &db),
|
||||||
) {
|
) {
|
||||||
(Ok(_), Ok(_)) => (),
|
(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")
|
bot.send_message(msg.chat.id, "Everything was deleted")
|
||||||
.reply_markup(deletion_markup())
|
.reply_markup(deletion_markup())
|
@ -1,9 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
entity::prelude::Account,
|
|
||||||
errors::NoUserInfo,
|
errors::NoUserInfo,
|
||||||
handlers::{markups::deletion_markup, Handler, MainDialogue, State},
|
markups::deletion_markup,
|
||||||
models::{DecryptedAccount, User},
|
models::{DecryptedAccount, User},
|
||||||
|
Handler, MainDialogue, State,
|
||||||
};
|
};
|
||||||
|
use entity::prelude::*;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use serde_json::to_vec_pretty;
|
use serde_json::to_vec_pretty;
|
||||||
@ -26,6 +27,7 @@ async fn get_master_pass(
|
|||||||
let master_pass: Arc<str> = master_pass.into();
|
let master_pass: Arc<str> = master_pass.into();
|
||||||
Account::get_all(user_id, &db)
|
Account::get_all(user_id, &db)
|
||||||
.await?
|
.await?
|
||||||
|
.err_into::<crate::Error>()
|
||||||
.try_for_each_concurrent(None, |account| {
|
.try_for_each_concurrent(None, |account| {
|
||||||
let master_pass = Arc::clone(&master_pass);
|
let master_pass = Arc::clone(&master_pass);
|
||||||
async move {
|
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::{
|
use crate::{
|
||||||
entity::prelude::Account,
|
|
||||||
errors::NoUserInfo,
|
errors::NoUserInfo,
|
||||||
handlers::{
|
markups::{self, deletion_markup},
|
||||||
markups::{self, deletion_markup},
|
state::NameCheckKind,
|
||||||
state::NameCheckKind,
|
Handler, MainDialogue, State,
|
||||||
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 teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
|
||||||
use tokio::task::spawn_blocking;
|
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 futures::TryStreamExt;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::DatabaseConnection;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*, types::ParseMode};
|
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};
|
use teloxide::{adaptors::Throttle, prelude::*, utils::command::BotCommands};
|
||||||
|
|
||||||
/// Handles the help command by sending the passwords descryptions
|
/// Handles the help command by sending the passwords descryptions
|
@ -1,7 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::NoUserInfo,
|
errors::NoUserInfo, markups::deletion_markup, models::User, Handler, MainDialogue, State,
|
||||||
handlers::{markups::deletion_markup, Handler, MainDialogue, State},
|
|
||||||
models::User,
|
|
||||||
};
|
};
|
||||||
use futures::{future, stream::FuturesUnordered, StreamExt};
|
use futures::{future, stream::FuturesUnordered, StreamExt};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
@ -1,8 +1,6 @@
|
|||||||
use crate::{
|
use crate::{errors::NoUserInfo, markups::deletion_markup, Handler, MainDialogue, State};
|
||||||
entity::prelude::*,
|
use cryptography::prelude::*;
|
||||||
errors::NoUserInfo,
|
use entity::prelude::*;
|
||||||
handlers::{markups::deletion_markup, Handler, MainDialogue, State},
|
|
||||||
};
|
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
@ -20,7 +18,7 @@ async fn get_master_pass(
|
|||||||
let model = task::spawn_blocking(move || {
|
let model = task::spawn_blocking(move || {
|
||||||
master_pass::ActiveModel::from_unencrypted(user_id, &master_password)
|
master_pass::ActiveModel::from_unencrypted(user_id, &master_password)
|
||||||
})
|
})
|
||||||
.await??;
|
.await?;
|
||||||
model.insert(&db).await?;
|
model.insert(&db).await?;
|
||||||
bot.send_message(msg.chat.id, "Success")
|
bot.send_message(msg.chat.id, "Success")
|
||||||
.reply_markup(deletion_markup())
|
.reply_markup(deletion_markup())
|
@ -1,4 +1,4 @@
|
|||||||
use crate::handlers::markups::deletion_markup;
|
use crate::markups::deletion_markup;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
/// Handles the messages which weren't matched by any commands or states
|
/// 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 callbacks;
|
||||||
mod commands;
|
mod commands;
|
||||||
mod default;
|
mod default;
|
||||||
|
mod errors;
|
||||||
mod markups;
|
mod markups;
|
||||||
mod master_password_check;
|
mod master_password_check;
|
||||||
|
mod models;
|
||||||
mod state;
|
mod state;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
use anyhow::{Error, Result};
|
||||||
use commands::Command;
|
use commands::Command;
|
||||||
|
use futures::future::BoxFuture as PinnedFuture;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use state::{Handler, MainDialogue, State};
|
use state::{Handler, MainDialogue, State};
|
||||||
use teloxide::{
|
use teloxide::{
|
10
src/main.rs
10
src/main.rs
@ -1,13 +1,7 @@
|
|||||||
mod entity;
|
use anyhow::Result;
|
||||||
mod errors;
|
|
||||||
mod handlers;
|
|
||||||
mod models;
|
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use futures::future::BoxFuture as PinnedFuture;
|
|
||||||
use handlers::get_dispatcher;
|
|
||||||
use migration::{Migrator, MigratorTrait};
|
use migration::{Migrator, MigratorTrait};
|
||||||
|
use pass_manager::get_dispatcher;
|
||||||
use sea_orm::Database;
|
use sea_orm::Database;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::entity::prelude::Account;
|
use entity::prelude::Account;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, KeyboardMarkup};
|
use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, KeyboardMarkup};
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{entity::prelude::MasterPass, errors::NoUserInfo};
|
use crate::errors::NoUserInfo;
|
||||||
use sea_orm::prelude::*;
|
use entity::prelude::*;
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
use teloxide::{adaptors::Throttle, dispatching::DpHandlerDescription, prelude::*};
|
use teloxide::{adaptors::Throttle, dispatching::DpHandlerDescription, prelude::*};
|
||||||
|
|
||||||
use super::markups::deletion_markup;
|
use super::markups::deletion_markup;
|
@ -1,6 +1,7 @@
|
|||||||
//! Models to export and import the accounts
|
//! Models to export and import the accounts
|
||||||
|
|
||||||
use crate::entity::prelude::*;
|
use cryptography::prelude::*;
|
||||||
|
use entity::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -36,6 +37,7 @@ impl DecryptedAccount {
|
|||||||
&self.password,
|
&self.password,
|
||||||
master_pass,
|
master_pass,
|
||||||
)
|
)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the account's fields are valid
|
/// Returns true if the account's fields are valid
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
use crate::{
|
use crate::{errors::HandlerUsed, markups::deletion_markup, utils::delete_optional, PinnedFuture};
|
||||||
errors::HandlerUsed,
|
|
||||||
handlers::{markups::deletion_markup, utils::delete_optional},
|
|
||||||
PinnedFuture,
|
|
||||||
};
|
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
@ -12,7 +8,7 @@ pub async fn generic<F>(
|
|||||||
bot: Throttle<Bot>,
|
bot: Throttle<Bot>,
|
||||||
msg: Message,
|
msg: Message,
|
||||||
db: DatabaseConnection,
|
db: DatabaseConnection,
|
||||||
dialogue: crate::handlers::MainDialogue,
|
dialogue: crate::MainDialogue,
|
||||||
check: F,
|
check: F,
|
||||||
no_text_message: impl Into<String>,
|
no_text_message: impl Into<String>,
|
||||||
next: super::PackagedHandler<String>,
|
next: super::PackagedHandler<String>,
|
@ -1,13 +1,11 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
entity::prelude::Account,
|
|
||||||
errors::{HandlerUsed, NoUserInfo},
|
errors::{HandlerUsed, NoUserInfo},
|
||||||
handlers::{
|
markups::{account_markup, deletion_markup},
|
||||||
markups::{account_markup, deletion_markup},
|
utils::{delete_optional, validate_field},
|
||||||
utils::{delete_optional, validate_field},
|
MainDialogue,
|
||||||
MainDialogue,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use sea_orm::prelude::*;
|
use entity::prelude::*;
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
/// Specifies the kind of checks to be run during the account name validation
|
/// 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 sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, 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 log::error;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::DatabaseConnection;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ async fn check_master_pass(
|
|||||||
let is_valid = match model {
|
let is_valid = match model {
|
||||||
Some(model) => {
|
Some(model) => {
|
||||||
let master_pass = master_pass.to_owned();
|
let master_pass = master_pass.to_owned();
|
||||||
spawn_blocking(move || model.verify(&master_pass)).await??
|
spawn_blocking(move || model.verify(&master_pass)).await?
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
error!("User was put into the GetMasterPass state with no master password set");
|
error!("User was put into the GetMasterPass state with no master password set");
|
@ -1,7 +1,7 @@
|
|||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
||||||
|
|
||||||
use crate::handlers::{utils::validate_field, MainDialogue};
|
use crate::{utils::validate_field, MainDialogue};
|
||||||
|
|
||||||
/// Function to handle GetPassword state
|
/// Function to handle GetPassword state
|
||||||
pub async fn get_password(
|
pub async fn get_password(
|
@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::HandlerUsed,
|
errors::HandlerUsed, markups::deletion_markup, models::User, utils::delete_optional,
|
||||||
handlers::{markups::deletion_markup, utils::delete_optional, MainDialogue},
|
MainDialogue,
|
||||||
models::User,
|
|
||||||
};
|
};
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
@ -1,4 +1,4 @@
|
|||||||
use crate::handlers::MainDialogue;
|
use crate::MainDialogue;
|
||||||
use sea_orm::prelude::*;
|
use sea_orm::prelude::*;
|
||||||
use std::{future::Future, sync::Arc};
|
use std::{future::Future, sync::Arc};
|
||||||
use teloxide::{adaptors::Throttle, prelude::*};
|
use teloxide::{adaptors::Throttle, prelude::*};
|
Loading…
x
Reference in New Issue
Block a user