Implemented account altering
This commit is contained in:
		@@ -5,14 +5,14 @@ use rand::{rngs::OsRng, RngCore};
 | 
				
			|||||||
use sea_orm::ActiveValue::Set;
 | 
					use sea_orm::ActiveValue::Set;
 | 
				
			||||||
use sha2::Sha256;
 | 
					use sha2::Sha256;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Cipher {
 | 
					pub struct Cipher {
 | 
				
			||||||
    chacha: ChaCha20Poly1305,
 | 
					    chacha: ChaCha20Poly1305,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cipher {
 | 
					impl Cipher {
 | 
				
			||||||
    /// Creates a new cipher from a master password and the salt
 | 
					    /// Creates a new cipher from a master password and the salt
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    fn new(password: &[u8], salt: &[u8]) -> Self {
 | 
					    pub fn new(password: &[u8], salt: &[u8]) -> Self {
 | 
				
			||||||
        let key = pbkdf2_hmac_array::<Sha256, 32>(password, salt, 480000);
 | 
					        let key = pbkdf2_hmac_array::<Sha256, 32>(password, salt, 480000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
@@ -31,7 +31,7 @@ impl Cipher {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Decrypts the value with the current cipher. The 12 byte nonce is expected to be at the end of the value
 | 
					    /// Decrypts the value with the current cipher. The 12 byte nonce is expected to be at the end of the value
 | 
				
			||||||
    #[inline]
 | 
					    #[inline]
 | 
				
			||||||
    fn decrypt(&self, value: &[u8]) -> crate::Result<Vec<u8>> {
 | 
					    pub fn decrypt(&self, value: &[u8]) -> crate::Result<Vec<u8>> {
 | 
				
			||||||
        let (data, nonce) = value.split_at(value.len() - 12);
 | 
					        let (data, nonce) = value.split_at(value.len() - 12);
 | 
				
			||||||
        self.chacha.decrypt(nonce.into(), data).map_err(Into::into)
 | 
					        self.chacha.decrypt(nonce.into(), data).map_err(Into::into)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
 | 
					//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use futures::Stream;
 | 
					use futures::Stream;
 | 
				
			||||||
use sea_orm::{entity::prelude::*, QueryOrder, QuerySelect, Statement};
 | 
					use sea_orm::{entity::prelude::*, ActiveValue::Set, QueryOrder, QuerySelect, Statement};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
 | 
					#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
 | 
				
			||||||
#[sea_orm(table_name = "account")]
 | 
					#[sea_orm(table_name = "account")]
 | 
				
			||||||
@@ -88,6 +88,7 @@ impl Entity {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Gets a name by a hex of a SHA256 hash of the name
 | 
					    /// Gets a name by a hex of a SHA256 hash of the name
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
    pub async fn get_name_by_hash(
 | 
					    pub async fn get_name_by_hash(
 | 
				
			||||||
        user_id: u64,
 | 
					        user_id: u64,
 | 
				
			||||||
        hash: String,
 | 
					        hash: String,
 | 
				
			||||||
@@ -102,4 +103,38 @@ impl Entity {
 | 
				
			|||||||
        .map(|result| result.try_get_by_index(0))
 | 
					        .map(|result| result.try_get_by_index(0))
 | 
				
			||||||
        .transpose()
 | 
					        .transpose()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    pub async fn get_salt(
 | 
				
			||||||
 | 
					        user_id: u64,
 | 
				
			||||||
 | 
					        name: String,
 | 
				
			||||||
 | 
					        db: &DatabaseConnection,
 | 
				
			||||||
 | 
					    ) -> crate::Result<Option<Vec<u8>>> {
 | 
				
			||||||
 | 
					        Self::find_by_id((user_id, name))
 | 
				
			||||||
 | 
					            .select_only()
 | 
				
			||||||
 | 
					            .column(Column::Salt)
 | 
				
			||||||
 | 
					            .into_tuple()
 | 
				
			||||||
 | 
					            .one(db)
 | 
				
			||||||
 | 
					            .await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    pub async fn update_name(
 | 
				
			||||||
 | 
					        user_id: u64,
 | 
				
			||||||
 | 
					        original_name: String,
 | 
				
			||||||
 | 
					        new_name: String,
 | 
				
			||||||
 | 
					        db: &DatabaseConnection,
 | 
				
			||||||
 | 
					    ) -> crate::Result<()> {
 | 
				
			||||||
 | 
					        Self::update_many()
 | 
				
			||||||
 | 
					            .set(ActiveModel {
 | 
				
			||||||
 | 
					                name: Set(new_name),
 | 
				
			||||||
 | 
					                ..Default::default()
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .filter(Column::UserId.eq(user_id))
 | 
				
			||||||
 | 
					            .filter(Column::Name.eq(original_name))
 | 
				
			||||||
 | 
					            .exec(db)
 | 
				
			||||||
 | 
					            .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										127
									
								
								src/callbacks/alter.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/callbacks/alter.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
				
			|||||||
 | 
					use super::AlterableField::{self, *};
 | 
				
			||||||
 | 
					use crate::{change_state, prelude::*};
 | 
				
			||||||
 | 
					use account::ActiveModel;
 | 
				
			||||||
 | 
					use futures::TryFutureExt;
 | 
				
			||||||
 | 
					use sea_orm::ActiveValue::Set;
 | 
				
			||||||
 | 
					use tokio::{task::spawn_blocking, try_join};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[inline]
 | 
				
			||||||
 | 
					async fn update_account(
 | 
				
			||||||
 | 
					    user_id: u64,
 | 
				
			||||||
 | 
					    db: &DatabaseConnection,
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    field: AlterableField,
 | 
				
			||||||
 | 
					    field_value: String,
 | 
				
			||||||
 | 
					    master_pass: String,
 | 
				
			||||||
 | 
					) -> crate::Result<()> {
 | 
				
			||||||
 | 
					    if let Name = field {
 | 
				
			||||||
 | 
					        Account::update_name(user_id, name, field_value, db).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Ok(());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let salt = Account::get_salt(user_id, name.clone(), db).await?.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let field_value = spawn_blocking(move || {
 | 
				
			||||||
 | 
					        let cipher = Cipher::new(master_pass.as_bytes(), &salt);
 | 
				
			||||||
 | 
					        cipher.encrypt(field_value.as_bytes()).unwrap()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut model = ActiveModel {
 | 
				
			||||||
 | 
					        user_id: Set(user_id),
 | 
				
			||||||
 | 
					        name: Set(name),
 | 
				
			||||||
 | 
					        ..Default::default()
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match field {
 | 
				
			||||||
 | 
					        Login => model.enc_login = Set(field_value),
 | 
				
			||||||
 | 
					        Pass => model.enc_password = Set(field_value),
 | 
				
			||||||
 | 
					        _ => unreachable!(),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model.update(db).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[inline]
 | 
				
			||||||
 | 
					#[allow(clippy::too_many_arguments)]
 | 
				
			||||||
 | 
					async fn get_master_pass(
 | 
				
			||||||
 | 
					    bot: Throttle<Bot>,
 | 
				
			||||||
 | 
					    msg: Message,
 | 
				
			||||||
 | 
					    db: DatabaseConnection,
 | 
				
			||||||
 | 
					    dialogue: MainDialogue,
 | 
				
			||||||
 | 
					    mut ids: MessageIds,
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    field: AlterableField,
 | 
				
			||||||
 | 
					    field_value: String,
 | 
				
			||||||
 | 
					    master_pass: String,
 | 
				
			||||||
 | 
					) -> crate::Result<()> {
 | 
				
			||||||
 | 
					    dialogue.exit().await?;
 | 
				
			||||||
 | 
					    let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    update_account(user_id, &db, name, field, field_value, master_pass).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ids.alter_message(
 | 
				
			||||||
 | 
					        &bot,
 | 
				
			||||||
 | 
					        "Success. Choose the account to view",
 | 
				
			||||||
 | 
					        menu_markup("get", user_id, &db).await?,
 | 
				
			||||||
 | 
					        None,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					handler!(get_field(name:String, field:AlterableField, field_value:String), "Send the master password", State::GetMasterPass, get_master_pass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub async fn alter(
 | 
				
			||||||
 | 
					    bot: Throttle<Bot>,
 | 
				
			||||||
 | 
					    q: CallbackQuery,
 | 
				
			||||||
 | 
					    db: DatabaseConnection,
 | 
				
			||||||
 | 
					    dialogue: MainDialogue,
 | 
				
			||||||
 | 
					    (hash, field): (super::NameHash, AlterableField),
 | 
				
			||||||
 | 
					) -> crate::Result<()> {
 | 
				
			||||||
 | 
					    let user_id = q.from.id.0;
 | 
				
			||||||
 | 
					    let mut ids: MessageIds = q.message.as_ref().unwrap().into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let name = match name_from_hash(&db, user_id, &hash).await? {
 | 
				
			||||||
 | 
					        Some(name) => name,
 | 
				
			||||||
 | 
					        None => {
 | 
				
			||||||
 | 
					            try_join!(
 | 
				
			||||||
 | 
					                ids.alter_message(
 | 
				
			||||||
 | 
					                    &bot,
 | 
				
			||||||
 | 
					                    "Account wasn't found. Choose another one",
 | 
				
			||||||
 | 
					                    menu_markup("get", user_id, &db).await?,
 | 
				
			||||||
 | 
					                    None,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                bot.answer_callback_query(q.id).send().err_into()
 | 
				
			||||||
 | 
					            )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return Ok(());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let text = match field {
 | 
				
			||||||
 | 
					        Name => {
 | 
				
			||||||
 | 
					            change_state!(dialogue, ids, (name, field), State::GetNewName, get_field);
 | 
				
			||||||
 | 
					            "Send new account name"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Login => {
 | 
				
			||||||
 | 
					            change_state!(dialogue, ids, (name, field), State::GetLogin, get_field);
 | 
				
			||||||
 | 
					            "Send new account login"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Pass => {
 | 
				
			||||||
 | 
					            change_state!(dialogue, ids, (name, field), State::GetPassword, get_field);
 | 
				
			||||||
 | 
					            "Send new account password"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try_join!(
 | 
				
			||||||
 | 
					        ids.alter_message(&bot, text, None, None),
 | 
				
			||||||
 | 
					        bot.answer_callback_query(q.id).send().err_into()
 | 
				
			||||||
 | 
					    )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -66,5 +66,7 @@ pub async fn decrypt(
 | 
				
			|||||||
        .await?;
 | 
					        .await?;
 | 
				
			||||||
    bot.answer_callback_query(q.id).await?;
 | 
					    bot.answer_callback_query(q.id).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    change_state!(dialogue, msg, (name), State::GetMasterPass, get_master_pass)
 | 
					    change_state!(dialogue, msg, (name), State::GetMasterPass, get_master_pass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,5 +61,7 @@ pub async fn delete(
 | 
				
			|||||||
        (name),
 | 
					        (name),
 | 
				
			||||||
        State::GetMasterPass,
 | 
					        State::GetMasterPass,
 | 
				
			||||||
        get_master_pass
 | 
					        get_master_pass
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
//! This module consists of endpoints to handle callbacks
 | 
					//! This module consists of endpoints to handle callbacks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
crate::export_handlers!(decrypt, delete, delete_message, get, get_menu);
 | 
					crate::export_handlers!(decrypt, delete, delete_message, get, get_menu, alter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::errors::InvalidCommand;
 | 
					use crate::errors::InvalidCommand;
 | 
				
			||||||
use base64::{engine::general_purpose::STANDARD_NO_PAD as B64_ENGINE, Engine as _};
 | 
					use base64::{engine::general_purpose::STANDARD_NO_PAD as B64_ENGINE, Engine as _};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,5 +46,7 @@ pub async fn set_master_pass(
 | 
				
			|||||||
        (),
 | 
					        (),
 | 
				
			||||||
        State::GetNewMasterPass,
 | 
					        State::GetNewMasterPass,
 | 
				
			||||||
        get_master_pass
 | 
					        get_master_pass
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,6 @@ macro_rules! change_state {
 | 
				
			|||||||
                $previous,
 | 
					                $previous,
 | 
				
			||||||
            )))
 | 
					            )))
 | 
				
			||||||
            .await?;
 | 
					            .await?;
 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }};
 | 
					    }};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,7 +21,9 @@ macro_rules! first_handler {
 | 
				
			|||||||
        ) -> $crate::Result<()> {
 | 
					        ) -> $crate::Result<()> {
 | 
				
			||||||
            let previous = bot.send_message(msg.chat.id, $message).await?;
 | 
					            let previous = bot.send_message(msg.chat.id, $message).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $crate::change_state!(dialogue, &previous, (), $next_state, $next_func)
 | 
					            $crate::change_state!(dialogue, &previous, (), $next_state, $next_func);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -42,7 +43,9 @@ macro_rules! handler {
 | 
				
			|||||||
        ) -> $crate::Result<()> {
 | 
					        ) -> $crate::Result<()> {
 | 
				
			||||||
            ids.alter_message(&bot, $message, None, None).await?;
 | 
					            ids.alter_message(&bot, $message, None, None).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $crate::change_state!(dialogue, ids, ($($param),*), $next_state, $next_func)
 | 
					            $crate::change_state!(dialogue, ids, ($($param),*), $next_state, $next_func);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,7 +62,8 @@ fn get_dispatcher(
 | 
				
			|||||||
        .branch(case![CallbackCommand::DeleteMessage].endpoint(callbacks::delete_message))
 | 
					        .branch(case![CallbackCommand::DeleteMessage].endpoint(callbacks::delete_message))
 | 
				
			||||||
        .branch(case![CallbackCommand::Get(hash)].endpoint(callbacks::get))
 | 
					        .branch(case![CallbackCommand::Get(hash)].endpoint(callbacks::get))
 | 
				
			||||||
        .branch(case![CallbackCommand::Decrypt(hash)].endpoint(callbacks::decrypt))
 | 
					        .branch(case![CallbackCommand::Decrypt(hash)].endpoint(callbacks::decrypt))
 | 
				
			||||||
        .branch(case![CallbackCommand::DeleteAccount(hash)].endpoint(callbacks::delete));
 | 
					        .branch(case![CallbackCommand::DeleteAccount(hash)].endpoint(callbacks::delete))
 | 
				
			||||||
 | 
					        .branch(case![CallbackCommand::Alter(hash, field)].endpoint(callbacks::alter));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let handler = dptree::entry()
 | 
					    let handler = dptree::entry()
 | 
				
			||||||
        .enter_dialogue::<Update, InMemStorage<State>, State>()
 | 
					        .enter_dialogue::<Update, InMemStorage<State>, State>()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ pub async fn delete_message(bot: Throttle<Bot>, msg: Message) {
 | 
				
			|||||||
/// Returns true if the field is valid
 | 
					/// Returns true if the field is valid
 | 
				
			||||||
#[inline]
 | 
					#[inline]
 | 
				
			||||||
pub fn validate_field(field: &str) -> bool {
 | 
					pub fn validate_field(field: &str) -> bool {
 | 
				
			||||||
    if field.is_empty() {
 | 
					    if !(1..255).contains(&field.len()) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    field
 | 
					    field
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user