More clippy fixes

This commit is contained in:
StNicolay 2023-11-19 14:45:46 +03:00
parent 6ae745fcd4
commit bfd68194e6
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
17 changed files with 79 additions and 51 deletions

View File

@ -11,6 +11,14 @@ strip = true
[workspace] [workspace]
members = [".", "migration", "entity", "cryptography"] members = [".", "migration", "entity", "cryptography"]
[workspace.lints.clippy]
pedantic = "warn"
all = "warn"
nursery = "warn"
[lints]
workspace = true
[dependencies] [dependencies]
ahash = "0.8.3" ahash = "0.8.3"
anyhow = "1" anyhow = "1"

View File

@ -5,6 +5,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lints]
workspace = true
[dependencies] [dependencies]
sha2 = "0.10" sha2 = "0.10"
scrypt = { version = "0.11", default-features = false, features = ["std"] } scrypt = { version = "0.11", default-features = false, features = ["std"] }

View File

@ -12,8 +12,9 @@ pub struct Cipher {
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]
#[must_use]
pub 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, 480_000);
Self { Self {
chacha: ChaCha20Poly1305::new(&key.into()), chacha: ChaCha20Poly1305::new(&key.into()),
@ -33,11 +34,12 @@ impl Cipher {
#[inline] #[inline]
pub 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)
} }
} }
pub trait AccountFromUnencryptedExt { pub trait FromUnencryptedExt {
fn from_unencrypted( fn from_unencrypted(
user_id: u64, user_id: u64,
name: String, name: String,
@ -47,8 +49,8 @@ pub trait AccountFromUnencryptedExt {
) -> crate::Result<account::ActiveModel>; ) -> crate::Result<account::ActiveModel>;
} }
impl AccountFromUnencryptedExt for account::ActiveModel { impl FromUnencryptedExt for account::ActiveModel {
/// Encryptes the provided data by the master password and creates the ActiveModel with all fields set to Set variant /// Encryptes the provided data by the master password and creates the `ActiveModel` with all fields set to Set variant
#[inline] #[inline]
fn from_unencrypted( fn from_unencrypted(
user_id: u64, user_id: u64,

View File

@ -11,6 +11,8 @@ static PARAMS: Lazy<Params> = Lazy::new(|| Params::new(14, 8, 1, HASH_LENGTH).un
/// Hashes the bytes with Scrypt with the given salt /// Hashes the bytes with Scrypt with the given salt
#[inline] #[inline]
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn hash_scrypt(bytes: &[u8], salt: &[u8]) -> [u8; HASH_LENGTH] { pub fn hash_scrypt(bytes: &[u8], salt: &[u8]) -> [u8; HASH_LENGTH] {
let mut hash = [0; HASH_LENGTH]; let mut hash = [0; HASH_LENGTH];
scrypt(bytes, salt, &PARAMS, &mut hash).unwrap(); scrypt(bytes, salt, &PARAMS, &mut hash).unwrap();
@ -29,6 +31,7 @@ where
impl HashedBytes<[u8; HASH_LENGTH], [u8; SALT_LENGTH]> { impl HashedBytes<[u8; HASH_LENGTH], [u8; SALT_LENGTH]> {
#[inline] #[inline]
#[must_use]
pub fn new(bytes: &[u8]) -> Self { pub fn new(bytes: &[u8]) -> Self {
let mut salt = [0; 64]; let mut salt = [0; 64];
OsRng.fill_bytes(&mut salt); OsRng.fill_bytes(&mut salt);
@ -45,6 +48,7 @@ where
U: AsRef<[u8]>, U: AsRef<[u8]>,
{ {
#[inline] #[inline]
#[must_use]
pub fn verify(&self, bytes: &[u8]) -> bool { pub fn verify(&self, bytes: &[u8]) -> bool {
let hash = hash_scrypt(bytes, self.salt.as_ref()); let hash = hash_scrypt(bytes, self.salt.as_ref());
hash.ct_eq(self.hash.as_ref()).into() hash.ct_eq(self.hash.as_ref()).into()

View File

@ -1,4 +1,5 @@
//! Functions to encrypt the database models //! Functions to encrypt the database models
#![allow(clippy::missing_errors_doc)]
pub mod account; pub mod account;
pub mod hashing; pub mod hashing;

View File

@ -2,12 +2,12 @@ use super::hashing::HashedBytes;
use entity::master_pass; use entity::master_pass;
use sea_orm::ActiveValue::Set; use sea_orm::ActiveValue::Set;
pub trait MasterPassFromUnencryptedExt { pub trait FromUnencryptedExt {
fn from_unencrypted(user_id: u64, password: &str) -> master_pass::ActiveModel; fn from_unencrypted(user_id: u64, password: &str) -> master_pass::ActiveModel;
} }
impl MasterPassFromUnencryptedExt for master_pass::ActiveModel { impl FromUnencryptedExt for master_pass::ActiveModel {
/// Hashes the password and creates an ActiveModel with all fields set to Set variant /// Hashes the password and creates an `ActiveModel` with all fields set to Set variant
#[inline] #[inline]
fn from_unencrypted(user_id: u64, password: &str) -> Self { fn from_unencrypted(user_id: u64, password: &str) -> Self {
let hash = HashedBytes::new(password.as_bytes()); let hash = HashedBytes::new(password.as_bytes());

View File

@ -25,6 +25,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 uppercase, one number and one punctuation char /// It checks that it has at least one lowercase, one uppercase, one number and one punctuation char
#[inline] #[inline]
#[must_use]
fn check_generated_password<const LENGTH: usize>(password: &[u8; LENGTH]) -> bool { fn check_generated_password<const LENGTH: usize>(password: &[u8; LENGTH]) -> bool {
let mut flags = PasswordFlags::empty(); let mut flags = PasswordFlags::empty();
for &byte in password { for &byte in password {
@ -33,7 +34,7 @@ fn check_generated_password<const LENGTH: usize>(password: &[u8; LENGTH]) -> boo
b'A'..=b'Z' => flags |= PasswordFlags::UPPERCASE, b'A'..=b'Z' => flags |= PasswordFlags::UPPERCASE,
b'0'..=b'9' => flags |= PasswordFlags::NUMBER, b'0'..=b'9' => flags |= PasswordFlags::NUMBER,
b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~' => { b'!'..=b'/' | b':'..=b'@' | b'['..=b'`' | b'{'..=b'~' => {
flags |= PasswordFlags::SPECIAL_CHARACTER flags |= PasswordFlags::SPECIAL_CHARACTER;
} }
_ => (), _ => (),
} }
@ -46,6 +47,7 @@ fn check_generated_password<const LENGTH: usize>(password: &[u8; LENGTH]) -> boo
/// Continuously generates the password until it passes the checks /// Continuously generates the password until it passes the checks
#[inline] #[inline]
#[must_use]
fn generate_password<R, const LENGTH: usize>(rng: &mut R) -> ArrayString<LENGTH> fn generate_password<R, const LENGTH: usize>(rng: &mut R) -> ArrayString<LENGTH>
where where
R: Rng + CryptoRng, R: Rng + CryptoRng,
@ -59,6 +61,8 @@ where
} }
#[inline] #[inline]
#[must_use]
#[allow(clippy::module_name_repetitions)]
pub fn generate_passwords<const AMOUNT: usize, const LENGTH: usize>( pub fn generate_passwords<const AMOUNT: usize, const LENGTH: usize>(
) -> [ArrayString<LENGTH>; AMOUNT] { ) -> [ArrayString<LENGTH>; AMOUNT] {
let mut rng = thread_rng(); let mut rng = thread_rng();
@ -66,6 +70,7 @@ pub fn generate_passwords<const AMOUNT: usize, const LENGTH: usize>(
} }
#[inline] #[inline]
#[must_use]
pub fn check_master_pass(password: &str) -> PasswordValidity { pub fn check_master_pass(password: &str) -> PasswordValidity {
let mut count = 0; let mut count = 0;
let mut chars = password.chars(); let mut chars = password.chars();
@ -74,13 +79,13 @@ pub fn check_master_pass(password: &str) -> PasswordValidity {
for char in &mut chars { for char in &mut chars {
count += 1; count += 1;
if char.is_lowercase() { if char.is_lowercase() {
flags.remove(PasswordValidity::NO_LOWERCASE) flags.remove(PasswordValidity::NO_LOWERCASE);
} else if char.is_uppercase() { } else if char.is_uppercase() {
flags.remove(PasswordValidity::NO_UPPERCASE) flags.remove(PasswordValidity::NO_UPPERCASE);
} else if char.is_ascii_digit() { } else if char.is_ascii_digit() {
flags.remove(PasswordValidity::NO_NUMBER) flags.remove(PasswordValidity::NO_NUMBER);
} else if char.is_ascii_punctuation() { } else if char.is_ascii_punctuation() {
flags.remove(PasswordValidity::NO_SPECIAL_CHARACTER) flags.remove(PasswordValidity::NO_SPECIAL_CHARACTER);
} }
if flags == PasswordValidity::TOO_SHORT { if flags == PasswordValidity::TOO_SHORT {
@ -90,7 +95,7 @@ pub fn check_master_pass(password: &str) -> PasswordValidity {
} }
if count >= 8 { if count >= 8 {
flags.remove(PasswordValidity::TOO_SHORT) flags.remove(PasswordValidity::TOO_SHORT);
} }
flags flags
@ -102,6 +107,6 @@ mod tests {
#[test] #[test]
fn chars_must_be_ascii() { fn chars_must_be_ascii() {
assert!(CHARS.is_ascii()) assert!(CHARS.is_ascii());
} }
} }

View File

@ -1,2 +1,2 @@
pub use crate::account::*; pub use crate::account::{DecryptAccountExt as _, FromUnencryptedExt as _};
pub use crate::master_pass::*; pub use crate::master_pass::FromUnencryptedExt as _;

View File

@ -5,6 +5,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lints]
workspace = true
[dependencies] [dependencies]
futures = "0.3" futures = "0.3"
sea-orm = "0.12" sea-orm = "0.12"

View File

@ -56,7 +56,7 @@ impl Entity {
#[inline] #[inline]
pub async fn exists( pub async fn exists(
user_id: u64, user_id: u64,
account_name: impl Into<String>, account_name: impl Into<String> + Send,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> crate::Result<bool> { ) -> crate::Result<bool> {
let count = Self::find_by_id((user_id, account_name.into())) let count = Self::find_by_id((user_id, account_name.into()))
@ -69,7 +69,7 @@ impl Entity {
#[inline] #[inline]
pub async fn get( pub async fn get(
user_id: u64, user_id: u64,
account_name: impl Into<String>, account_name: impl Into<String> + Send,
db: &DatabaseConnection, db: &DatabaseConnection,
) -> crate::Result<Option<Model>> { ) -> crate::Result<Option<Model>> {
Self::find_by_id((user_id, account_name.into())) Self::find_by_id((user_id, account_name.into()))

View File

@ -1,3 +1,6 @@
// This is fine, because all errors can only be caused by the database errors and the docs would get repetative very quickly
#![allow(clippy::missing_errors_doc)]
pub mod account; pub mod account;
pub mod master_pass; pub mod master_pass;
pub mod prelude; pub mod prelude;

View File

@ -3,6 +3,9 @@ name = "migration"
version = "0.2.0" version = "0.2.0"
edition = "2021" edition = "2021"
[lints]
workspace = true
[dependencies.sea-orm-migration] [dependencies.sea-orm-migration]
version = "0.12" version = "0.12"
features = ["runtime-tokio-rustls", "sqlx-mysql"] features = ["runtime-tokio-rustls", "sqlx-mysql"]

View File

@ -1,6 +1,7 @@
use super::AlterableField::{self, Login, Name, Pass}; use super::AlterableField::{self, Login, Name, Pass};
use crate::{change_state, prelude::*}; use crate::{change_state, prelude::*};
use account::ActiveModel; use account::ActiveModel;
use cryptography::account::Cipher;
use futures::TryFutureExt; use futures::TryFutureExt;
use sea_orm::ActiveValue::Set; use sea_orm::ActiveValue::Set;
use tokio::{task::spawn_blocking, try_join}; use tokio::{task::spawn_blocking, try_join};

View File

@ -12,15 +12,16 @@ pub async fn get_accounts(
) -> crate::Result<()> { ) -> crate::Result<()> {
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let mut account_names = Account::get_names(user_id, &db).await?; let mut account_names = Account::get_names(user_id, &db).await?;
let mut text = match account_names.try_next().await? {
Some(name) => format!("Accounts:\n`{name}`"), let mut text = if let Some(name) = account_names.try_next().await? {
None => { format!("Accounts:\n`{name}`")
bot.send_message(msg.chat.id, "No accounts found") } else {
.reply_markup(deletion_markup()) bot.send_message(msg.chat.id, "No accounts found")
.await?; .reply_markup(deletion_markup())
return Ok(()); .await?;
} return Ok(());
}; };
account_names account_names
.map_err(crate::Error::from) .map_err(crate::Error::from)
.try_for_each(|name| { .try_for_each(|name| {
@ -28,6 +29,7 @@ pub async fn get_accounts(
future::ready(result) future::ready(result)
}) })
.await?; .await?;
bot.send_message(msg.chat.id, text) bot.send_message(msg.chat.id, text)
.parse_mode(ParseMode::MarkdownV2) .parse_mode(ParseMode::MarkdownV2)
.reply_markup(deletion_markup()) .reply_markup(deletion_markup())

View File

@ -1,5 +1,5 @@
#![warn(clippy::pedantic, clippy::all, clippy::nursery)] // #![warn(clippy::pedantic, clippy::all, clippy::nursery)]
#![allow(clippy::single_match_else)] // #![allow(clippy::single_match_else)]
mod callbacks; mod callbacks;
mod commands; mod commands;

View File

@ -26,15 +26,12 @@ where
return Err(HandlerUsed.into()); return Err(HandlerUsed.into());
} }
let text = match msg.text() { let Some(text) = msg.text().map(str::trim) else {
Some(text) => text.trim(), handler
None => { .previous
handler .alter_message(&bot, no_text_message, None, None)
.previous .await?;
.alter_message(&bot, no_text_message, None, None) return Ok(());
.await?;
return Ok(());
}
}; };
if text == "/cancel" { if text == "/cancel" {

View File

@ -11,21 +11,17 @@ async fn check_master_pass(
master_pass: &str, master_pass: &str,
) -> crate::Result<Option<String>> { ) -> crate::Result<Option<String>> {
let user_id = msg.from().ok_or(NoUserInfo)?.id.0; let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let model = MasterPass::get(user_id, db).await?; let Some(model) = MasterPass::get(user_id, db).await? else {
error!("User was put into the GetMasterPass state with no master password set");
return Ok(Some(
"No master password set. Use /cancel and set it by using /set_master_pass".to_owned(),
));
};
let is_valid = match model { let is_valid = {
Some(model) => { let hash = HashedBytes::from(model);
let hash: HashedBytes<_, _> = model.into(); let master_pass = master_pass.to_owned();
let master_pass = master_pass.to_owned(); spawn_blocking(move || hash.verify(master_pass.as_bytes())).await?
spawn_blocking(move || hash.verify(master_pass.as_bytes())).await?
}
None => {
error!("User was put into the GetMasterPass state with no master password set");
return Ok(Some(
"No master password set. Use /cancel and set it by using /set_master_pass"
.to_owned(),
));
}
}; };
if !is_valid { if !is_valid {