From 693426f0411d780c15a6c9e9222533ab5f4b690f Mon Sep 17 00:00:00 2001 From: EETagent Date: Fri, 11 Nov 2022 20:20:48 +0100 Subject: [PATCH] feat: add useful utilities --- Cargo.lock | 41 ++++++++++++- recovery/Cargo.toml | 1 + recovery/src/main.rs | 143 +++++++++++++++++++++++++++++++------------ 3 files changed, 144 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0bb73e5..78e68ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,7 +409,7 @@ dependencies = [ "atty", "bitflags", "clap_derive", - "clap_lex", + "clap_lex 0.2.4", "indexmap", "once_cell", "strsim", @@ -417,6 +417,20 @@ dependencies = [ "textwrap", ] +[[package]] +name = "clap" +version = "4.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb41c13df48950b20eb4cd0eefa618819469df1bffc49d11e8487c4ba0037e5" +dependencies = [ + "atty", + "bitflags", + "clap_lex 0.3.0", + "once_cell", + "strsim", + "termcolor", +] + [[package]] name = "clap_derive" version = "3.2.18" @@ -439,6 +453,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1893,6 +1916,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "portfolio-recovery" +version = "0.1.0" +dependencies = [ + "clap 4.0.23", + "portfolio-core", + "portfolio-entity", + "sea-orm", + "tokio", + "url", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2339,7 +2374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "882af0d4cd3d6cc2f427d14a48e9f468b37c563b405ea486fd314ba18ca334d0" dependencies = [ "chrono", - "clap", + "clap 3.2.23", "dotenvy", "regex", "sea-schema", @@ -2368,7 +2403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86fe6e594b078712e1e797b951b9b56e55d3cfa04aac8ea76eb4bed7c94c5910" dependencies = [ "async-trait", - "clap", + "clap 3.2.23", "dotenvy", "sea-orm", "sea-orm-cli", diff --git a/recovery/Cargo.toml b/recovery/Cargo.toml index d2ecc52..278add3 100644 --- a/recovery/Cargo.toml +++ b/recovery/Cargo.toml @@ -9,6 +9,7 @@ url = "^2.3" clap = { version = "^4.0", features = ["cargo"] } portfolio-entity = { path = "../entity" } +portfolio-core = { path = "../core" } [dependencies.tokio] version = "^1.21" diff --git a/recovery/src/main.rs b/recovery/src/main.rs index 1d49a5d..357258c 100644 --- a/recovery/src/main.rs +++ b/recovery/src/main.rs @@ -1,60 +1,127 @@ use std::path::PathBuf; use url::Url; -use clap::{arg, command, value_parser}; +use clap::{arg, command, value_parser, Command, ArgAction}; use sea_orm::{Database, DatabaseConnection}; -use sea_orm::*; use ::entity::candidate::Entity as Candidate; use ::entity::parent::Entity as Parent; +use sea_orm::*; #[tokio::main] async fn main() -> Result<(), Box> { let clap = command!() - .arg( - arg!( - -d --database "URL to the database or sql file with postgres:// or sqlite://" - ) - .alias("url") - .required(true) - .value_parser(value_parser!(Url)), + .propagate_version(true) + .subcommand_required(true) + .arg_required_else_help(true) + .subcommand( + Command::new("portfolio") + .about("Database & Portfolio operations") + .arg( + arg!( + -d --database "URL to the database or sql file with postgres:// or sqlite://" + ) + .alias("url") + .required(true) + .value_parser(value_parser!(Url)), + ) + .arg( + arg!( + -p --portfolio "Path to the portfolio root" + ) + .required(true) + .value_parser(value_parser!(PathBuf)), + ) + .arg( + arg!( + -k --key "AGE private key for decryption" + ) + .required(true), + ) ) - .arg( - arg!( - -p --portfolio "Path to the portfolio root" - ) - .required(true) - .value_parser(value_parser!(PathBuf)), + .subcommand( + Command::new("hash") + .about("Hash operations") + .arg( + arg!( + -i --input "Plaintext to hash" + ) + .required(true) + ), ) - .arg( - arg!( - -k --key "AGE private key for decryption" - ) - .required(true), + .subcommand( + Command::new("encryption") + .about("Encryption operations") + .arg( + arg!( + -d --decrypt ... "Decrypt flag" + ) + .action(ArgAction::SetFalse) + .required(false) + ) + .arg( + arg!( + -i --input "Plaintext to encrypt/decrypt" + ) + .required(true) + ) + .arg( + arg!( + -k --key "Key for encryption/decryption" + ) + .required(true), + ) ) .get_matches(); - let sqlite_url = clap.get_one::("database").unwrap(); + match clap.subcommand() { + Some(("portfolio", sub_matches)) => { + let sqlite_url = sub_matches.get_one::("database").unwrap(); - println!("Connecting to {:?}", sqlite_url); + println!("Connecting to {:?}", sqlite_url); - if sqlite_url.scheme() != "sqlite" && sqlite_url.scheme() != "postgres" { - return Err("URL scheme postgres:// or sqlite:// required")?; + if sqlite_url.scheme() != "sqlite" && sqlite_url.scheme() != "postgres" { + return Err("URL scheme postgres:// or sqlite:// required")?; + } + + let db: DatabaseConnection = Database::connect(sqlite_url.as_str()).await?; + + let entries = Candidate::find() + .join_rev( + JoinType::InnerJoin, + Parent::belongs_to(Candidate) + .from(::entity::parent::Column::Application) + .to(::entity::candidate::Column::Application) + .into(), + ) + .all(&db) + .await?; + + println!("Found {} entries", entries.len()); + } + Some(("hash", sub_matches)) => { + let input = sub_matches.get_one::("input").unwrap(); + + let hash = portfolio_core::crypto::hash_password(input.to_string()).await?; + + println!("{}", hash); + } + Some(("encryption", sub_matches)) => { + let decrypt = sub_matches.get_one::("decrypt").unwrap(); + let input = sub_matches.get_one::("input").unwrap(); + let key = sub_matches.get_one::("key").unwrap(); + + + let result = if !*decrypt { + portfolio_core::crypto::decrypt_password(input.to_string(), key.to_string()).await? + } else { + portfolio_core::crypto::encrypt_password(input.to_string(), key.to_string()).await? + }; + + println!("{}", result); + } + _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), } - let db: DatabaseConnection = Database::connect(sqlite_url.as_str()).await?; - - let entries = Candidate::find() - .join_rev( - JoinType::InnerJoin, - Parent::belongs_to(Candidate) - .from(::entity::parent::Column::Application) - .to(::entity::candidate::Column::Application) - .into(), - ) - .all(&db) - .await?; - Ok(()) - }