diff --git a/Cargo.lock b/Cargo.lock index 415b533..f6b2faf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,7 +444,7 @@ dependencies = [ [[package]] name = "bichon" -version = "0.3.3" +version = "0.3.4" dependencies = [ "ahash", "async-imap", @@ -471,7 +471,7 @@ dependencies = [ "imap-proto", "itertools", "itoa", - "lru 0.16.2", + "lru 0.16.3", "mail-parser", "mail-send", "memmap2 0.9.9", @@ -501,7 +501,7 @@ dependencies = [ "sysinfo", "tantivy", "tempfile", - "time 0.3.44", + "time 0.3.45", "timeago", "tokio", "tokio-io-timeout", @@ -747,9 +747,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", "js-sys", @@ -1482,16 +1482,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - [[package]] name = "futures" version = "0.3.31" @@ -1853,7 +1843,7 @@ dependencies = [ "rustls", "rustls-pki-types", "thiserror 2.0.17", - "time 0.3.44", + "time 0.3.45", "tinyvec", "tokio", "tokio-rustls", @@ -1915,9 +1905,9 @@ dependencies = [ [[package]] name = "html2text" -version = "0.16.5" +version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89f3074c1e4a7c4b4f7aca411a610e0f2f27e16d571d0e584524b66c93204100" +checksum = "9ea4ba4c0f993633569337a4fadfbd4f27448c81adb1af0c2407065f52809662" dependencies = [ "html5ever", "tendril", @@ -1927,9 +1917,9 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.36.1" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +checksum = "5935f02fdc02823ff15fec27c2b3d7ca19d629e996f7a0ae4d7d500e62e54c76" dependencies = [ "log", "markup5ever", @@ -2520,9 +2510,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ "hashbrown 0.16.1", ] @@ -2558,12 +2548,6 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - [[package]] name = "mail-auth" version = "0.7.5" @@ -2627,9 +2611,9 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.36.1" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +checksum = "7cfb33ea12d5d83b1ba9a55ae7d05faec4f2189d47b79c04d4cea6bbe9f5b083" dependencies = [ "log", "tendril", @@ -3836,9 +3820,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.9.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -3908,9 +3892,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "aws-lc-rs", "log", @@ -3933,9 +3917,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -4064,9 +4048,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", @@ -4455,7 +4439,6 @@ dependencies = [ "parking_lot", "phf_shared 0.13.1", "precomputed-hash", - "serde", ] [[package]] @@ -4617,7 +4600,7 @@ dependencies = [ "tantivy-tokenizer-api", "tempfile", "thiserror 2.0.17", - "time 0.3.44", + "time 0.3.45", "uuid", "winapi", "zstd", @@ -4658,7 +4641,7 @@ dependencies = [ "byteorder", "ownedbytes", "serde", - "time 0.3.44", + "time 0.3.45", ] [[package]] @@ -4732,12 +4715,11 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" dependencies = [ - "futf", - "mac", + "new_debug_unreachable", "utf-8", ] @@ -4807,9 +4789,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", @@ -4817,16 +4799,16 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", - "time-macros 0.2.24", + "time-macros 0.2.25", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" @@ -4840,9 +4822,9 @@ dependencies = [ [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -5096,7 +5078,7 @@ checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" dependencies = [ "crossbeam-channel", "thiserror 2.0.17", - "time 0.3.44", + "time 0.3.45", "tracing-subscriber", ] @@ -5535,9 +5517,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd0c322f146d0f8aad130ce6c187953889359584497dac6561204c8e17bb43d" +checksum = "30e588f10c7bc3465f5fc1ab087fc97877ec1064a7ec89fb685ac4ee998dac4a" dependencies = [ "phf 0.13.1", "phf_codegen", @@ -5547,9 +5529,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" dependencies = [ "rustls-pki-types", ] @@ -6208,7 +6190,7 @@ dependencies = [ "pbkdf2", "ppmd-rust", "sha1 0.10.6", - "time 0.3.44", + "time 0.3.45", "zeroize", "zopfli", "zstd", diff --git a/Cargo.toml b/Cargo.toml index fb44ae0..a752e1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bichon" -version = "0.3.3" +version = "0.3.4" edition = "2021" [[bin]] @@ -11,6 +11,11 @@ path = "src/main.rs" name = "bichonctl" path = "src/bin/bichonctl.rs" +[[bin]] +name = "bichon-admin" +path = "src/bin/bichon_admin.rs" + + [features] default = [] vendored-openssl = ["openssl-sys"] @@ -22,7 +27,7 @@ opt-level = 3 codegen-units = 1 [dependencies] -chrono = "0.4.42" +chrono = "0.4.43" clap = { version = "4.5.54", features = ["derive", "env"] } mimalloc = "0.1.48" native_db = "0.8.2" @@ -40,7 +45,7 @@ poem-openapi = { version = "5.1.16", features = [ ] } ring = { version = "0.17.14", features = ["std"] } serde = { version = "1.0.228", features = ["derive"] } -serde_json = "1.0.148" +serde_json = "1.0.149" tokio = { version = "1.49.0", features = ["full"] } tracing = "0.1.44" tracing-appender = "0.2.3" @@ -84,22 +89,22 @@ async-imap = { version = "0.11.1", default-features = false, features = [ "runtime-tokio", "compress", ] } -webpki-roots = "1.0.4" -rustls = { version = "0.23.35", default-features = false, features = ["ring"] } -rustls-pki-types = "1.13.2" +webpki-roots = "1.0.5" +rustls = { version = "0.23.36", default-features = false, features = ["ring"] } +rustls-pki-types = "1.14.0" tokio-io-timeout = "1.2.1" bb8 = "0.9.1" semver = "1.0.27" governor = "0.10.4" -lru = "0.16.2" +lru = "0.16.3" mime_guess = "2.0.5" hex = "0.4.3" -time = { version = "0.3.44", features = [ +time = { version = "0.3.45", features = [ "formatting", "parsing", "local-offset", ] } -rust-embed = "8.9.0" +rust-embed = "8.11.0" murmur3 = "0.5.2" autoconfig = "0.4.0" urlencoding = "2.1.3" @@ -109,7 +114,7 @@ openssl-sys = { version = "0.9.111", optional = true, features = ["vendored"] } gethostname = "1.1.0" tantivy = { version = "0.25.0", features = ["quickwit", "zstd-compression"] } itoa = "1.0.17" -html2text = "0.16.5" +html2text = "0.16.6" bytes = "1.11.0" dialoguer = "0.12.0" console = "0.16.2" diff --git a/src/bin/bichon_admin.rs b/src/bin/bichon_admin.rs new file mode 100644 index 0000000..a835499 --- /dev/null +++ b/src/bin/bichon_admin.rs @@ -0,0 +1,301 @@ +use std::{ + fs, + path::{Path, PathBuf}, + rc::Rc, +}; + +use bichon::modules::{ + cli::admin::meta::{find_admin, init_meta_database, update_admin_password}, + error::BichonError, + utils::encrypt::internal_decrypt_string, +}; +use console::{style, Emoji}; +use dialoguer::Confirm; +use dialoguer::{theme::ColorfulTheme, Input, Password, Select}; +use native_db::Database; + +#[tokio::main] +async fn main() { + let theme = ColorfulTheme::default(); + println!( + "\n{}\n", + style("BICHON ADMINISTRATIVE TOOL").bold().bright().cyan() + ); + + let main_options = vec!["Reset Admin Password", "Exit"]; + let selection = Select::with_theme(&theme) + .with_prompt("Select an operation") + .default(0) + .items(&main_options) + .interact() + .unwrap(); + + if selection == 1 { + println!("{}", style("Exiting...").dim()); + return; + } + + let root_dir_str: String = Input::with_theme(&theme) + .with_prompt("Enter the absolute path for 'bichon_root_dir'") + .validate_with(|input: &String| -> Result<(), &str> { + let path = Path::new(input); + if !path.is_absolute() { + return Err("Path must be absolute."); + } + if !path.exists() { + return Err("Directory does not exist."); + } + let has_metadata = path.join("meta.db").exists(); + if !has_metadata { + return Err("Invalid directory: 'meta.db' not found."); + } + Ok(()) + }) + .interact_text() + .unwrap(); + + let root_path = PathBuf::from(&root_dir_str); + + let database: Rc> = match init_meta_database(&root_path.join("meta.db")) { + Ok(database) => database, + Err(e) => match e { + BichonError::Generic { + message, + location, + code, + } => { + if message.contains("RedbDatabaseError(DatabaseAlreadyOpen") { + println!("\n{}", style("ERROR: Database is locked.").red().bold()); + println!( + "{}", + style("The Bichon service is likely still running.").yellow() + ); + println!( + "Since the database cannot be shared between multiple instances, \n\ + you must {} the Bichon service before proceeding.", + style("STOP").underlined().bold() + ); + std::process::exit(1); + } else { + eprintln!( + "\n{} (Code: {:#?})\nLocation: {}\nMessage: {}", + style("A database error occurred:").red().bold(), + code, + location, + message + ); + std::process::exit(1); + } + } + }, + }; + + let admin = find_admin(&database); + + match admin { + Ok(Some(user)) => { + println!("\n{}", style("Admin user found:").green().bold()); + println!("----------------------------------------"); + println!("{:<12} : {}", "Username", style(&user.username).cyan()); + println!("{:<12} : {}", "Email", style(&user.email).cyan()); + } + Ok(None) => { + println!( + "\n{}", + style("ERROR: No admin user found in the database.") + .red() + .bold() + ); + println!("Please ensure the system has been initialized correctly."); + std::process::exit(1); + } + Err(e) => { + eprintln!( + "\n{} Failed to query admin user.", + style("ERROR:").red().bold() + ); + eprintln!("Details: {:?}", e); + std::process::exit(1); + } + } + + let encryption_key = loop { + let auth_methods = vec![ + "Enter encryption password manually", + "Read from password file", + ]; + let method = Select::with_theme(&theme) + .with_prompt("How would you like to provide the database encryption key?") + .items(&auth_methods) + .interact() + .unwrap(); + + let raw_key = if method == 0 { + Password::with_theme(&theme) + .with_prompt("Enter Encryption Password") + .interact() + .unwrap() + } else { + let file_path: String = Input::with_theme(&theme) + .with_prompt("Enter path to encryption password file") + .interact_text() + .unwrap(); + + match fs::read_to_string(&file_path) { + Ok(content) => content.trim().to_string(), + Err(e) => { + println!("{}: {}", style("Failed to read file").red(), e); + continue; + } + } + }; + + if raw_key.is_empty() { + println!("{}", style("Key cannot be empty.").red()); + continue; + } + + let prompt_message = format!( + "Encryption key loaded: [ {} ]\n\n \ + {}: This key must match the database encryption key used by the server.\n \ + It corresponds to these settings in your service:\n \ + - Arguments: {} or {}\n \ + - Envs: {} or {}\n\n \ + Do you want to continue?", + style(&raw_key).cyan().bold(), + style("IMPORTANT").yellow().bold(), + style("--bichon_encrypt_password").italic(), + style("--bichon_encrypt_password_file").italic(), + style("BICHON_ENCRYPT_PASSWORD").green(), + style("BICHON_ENCRYPT_PASSWORD_FILE").green() + ); + + if Confirm::with_theme(&theme) + .with_prompt(prompt_message) + .default(true) + .interact() + .unwrap() + { + break raw_key; + } + }; + + let admin = find_admin(&database); + + match admin { + Ok(Some(user)) => { + println!("----------------------------------------"); + println!("{:<12} : {}", "Username", style(&user.username).cyan()); + println!("{:<12} : {}", "Email", style(&user.email).cyan()); + + let pwd_display = match &user.password { + Some(p) => { + let password = match internal_decrypt_string(&encryption_key, p) { + Ok(p) => p, + Err(e) => { + println!("\n{}", style("ERROR: Decryption Failed").red().bold()); + println!( + "{}", + style("The provided encryption key is incorrect or invalid for this database.").yellow() + ); + println!( + "{} Please verify your {} or the {} you provided.", + style("➔").cyan(), + style("encryption password").bold(), + style("key file").bold() + ); + eprintln!("\nTechnical details: {:?}", e); + std::process::exit(1); + } + }; + style(password).yellow().to_string() + } + None => style("None (No password set)").dim().italic().to_string(), + }; + + println!("{:<12} : {}", "Password", pwd_display); + println!("----------------------------------------"); + + if !dialoguer::Confirm::with_theme(&theme) + .with_prompt(format!( + "Do you want to reset the password for '{}'?", + user.username + )) + .interact() + .unwrap() + { + println!("Operation cancelled."); + return; + } + } + Ok(None) => { + println!( + "\n{}", + style("ERROR: No admin user found in the database.") + .red() + .bold() + ); + println!("Please ensure the system has been initialized correctly."); + std::process::exit(1); + } + Err(e) => { + eprintln!( + "\n{} Failed to query admin user.", + style("ERROR:").red().bold() + ); + eprintln!("Details: {:?}", e); + std::process::exit(1); + } + } + + println!( + "\n{}", + style("TARGET: Reset password for user 'admin'") + .yellow() + .bold() + ); + + let new_login_password = Password::with_theme(&theme) + .with_prompt("Enter new Admin Login Password") + .with_confirmation("Repeat password to confirm", "Passwords do not match!") + .interact() + .unwrap(); + + if !Confirm::with_theme(&theme) + .with_prompt("Proceed with database update?") + .interact() + .unwrap() + { + return; + } + + println!("\n{} {}", style("⌛").yellow(), "Updating database..."); + + match update_admin_password(&database, new_login_password, &encryption_key) { + Ok(_) => { + println!( + "\n{} {}", + Emoji("✨", "*"), + style("Success! Admin password has been updated.") + .green() + .bold() + ); + println!( + "{}", + style("You can now log in with the new password.").dim() + ); + } + Err(e) => { + println!( + "\n{}", + style("ERROR: Failed to update database").red().bold() + ); + eprintln!( + "{} Could not save the new password to the database.", + style("➔").cyan() + ); + eprintln!("\nDetails: {:?}", e); + std::process::exit(1); + } + } +} diff --git a/src/modules/cli/admin/meta.rs b/src/modules/cli/admin/meta.rs new file mode 100644 index 0000000..a987965 --- /dev/null +++ b/src/modules/cli/admin/meta.rs @@ -0,0 +1,111 @@ +use std::{path::Path, rc::Rc}; + +use native_db::{Builder, Database}; + +use crate::{ + modules::{ + database::META_MODELS, + error::{code::ErrorCode, BichonResult}, + token::{AccessTokenModel, AccessTokenModelKey, TokenType}, + users::{UserModel, DEFAULT_ADMIN_USER_ID}, + utils::encrypt::internal_encrypt_string, + }, + raise_error, +}; +use itertools::Itertools; + +pub fn init_meta_database(path: impl AsRef) -> BichonResult>> { + let database = Builder::new() + .set_cache_size(134217728) + .create(&META_MODELS, path) + .map_err(|e| { + raise_error!( + format!("Failed to open database: {:?}", e), + ErrorCode::InternalError + ) + })?; + + Ok(Rc::new(database)) +} + +pub fn find_admin(database: &Rc>) -> BichonResult> { + let r_transaction = database + .r_transaction() + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + let entity: Option = r_transaction + .get() + .primary(DEFAULT_ADMIN_USER_ID) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + return Ok(entity); +} + +pub fn update_admin_password( + database: &Rc>, + password: String, + encrypt_key: &str, +) -> BichonResult<()> { + let rw_transaction = database + .rw_transaction() + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + let entity: UserModel = rw_transaction + .get() + .primary(DEFAULT_ADMIN_USER_ID) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))? + .ok_or_else(|| raise_error!("admin is not found".into(), ErrorCode::InternalError))?; + + let mut updated = entity.clone(); + updated.password = Some( + internal_encrypt_string(encrypt_key, &password) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?, + ); + + rw_transaction + .update(entity, updated) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + rw_transaction + .commit() + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + return Ok(()); +} + +pub fn reset_webui_token(database: &Rc>) -> BichonResult<()> { + let rw_transaction = database + .rw_transaction() + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + + let tokens: Vec = rw_transaction + .scan() + .secondary(AccessTokenModelKey::user_id) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))? + .start_with(DEFAULT_ADMIN_USER_ID) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))? + .try_collect() + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + + let webui_token = tokens + .into_iter() + .find(|t| t.token_type == TokenType::WebUI); + + let new_token = AccessTokenModel::new_webui_token(DEFAULT_ADMIN_USER_ID); + match webui_token { + Some(current) => { + rw_transaction + .remove(current) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + + rw_transaction + .insert(new_token) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + } + None => { + rw_transaction + .insert(new_token) + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + } + } + + rw_transaction + .commit() + .map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))?; + Ok(()) +} diff --git a/src/modules/cli/admin/mod.rs b/src/modules/cli/admin/mod.rs new file mode 100644 index 0000000..6b50816 --- /dev/null +++ b/src/modules/cli/admin/mod.rs @@ -0,0 +1 @@ +pub mod meta; diff --git a/src/modules/cli/mod.rs b/src/modules/cli/mod.rs index 906f9ac..8671543 100644 --- a/src/modules/cli/mod.rs +++ b/src/modules/cli/mod.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::bichon_version; +pub mod admin; pub mod auth; pub mod eml; pub mod mbox; diff --git a/src/modules/settings/mod.rs b/src/modules/settings/mod.rs index 1e5cb77..7d28110 100644 --- a/src/modules/settings/mod.rs +++ b/src/modules/settings/mod.rs @@ -16,17 +16,15 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use crate::modules::settings::cli::Settings; use poem_openapi::Object; use serde::{Deserialize, Serialize}; -use crate::modules::settings::cli::Settings; - pub mod cli; pub mod dir; pub mod io; pub mod proxy; pub mod system; - #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize, Object)] pub struct SystemConfigurations { pub bichon_log_level: String, diff --git a/src/modules/users/mod.rs b/src/modules/users/mod.rs index 1bc8ba9..5df1b10 100644 --- a/src/modules/users/mod.rs +++ b/src/modules/users/mod.rs @@ -498,7 +498,6 @@ impl BichonUserV2 { pub async fn update(id: u64, request: UserUpdateRequest) -> BichonResult<()> { let _ = &request.validate().await?; let password_changed = request.password.is_some(); - // let is_default_admin = id == DEFAULT_ADMIN_USER_ID; if is_default_admin { diff --git a/src/modules/utils/encrypt.rs b/src/modules/utils/encrypt.rs index e39faab..7671962 100644 --- a/src/modules/utils/encrypt.rs +++ b/src/modules/utils/encrypt.rs @@ -72,7 +72,7 @@ pub fn decrypt_string(data: &str) -> BichonResult { }) } -fn internal_encrypt_string( +pub fn internal_encrypt_string( password: &str, plaintext: &str, ) -> Result { @@ -102,7 +102,7 @@ fn internal_encrypt_string( Ok(general_purpose::URL_SAFE.encode(&result)) } -fn internal_decrypt_string(password: &str, data: &str) -> Result { +pub fn internal_decrypt_string(password: &str, data: &str) -> Result { let data = general_purpose::URL_SAFE .decode(data) .map_err(|_| ring::error::Unspecified)?;