Implemented account altering
This commit is contained in:
parent
ab62e74cb7
commit
386f060a41
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user