use argon2::{ Argon2, PasswordHash, PasswordHasher, PasswordVerifier, password_hash::{SaltString, rand_core::OsRng}, }; use rand::distr::Alphanumeric; use rand::prelude::*; use rand_chacha::ChaCha20Rng; mod auth; mod email_token; mod model; mod routes; mod session; pub use auth::*; pub use routes::init_routes; pub use session::*; use crate::error::{ApiResult, Error}; pub fn csprng(take: usize) -> String { // Generate a CSPRNG 128-bit (16 byte) ID using alphanumeric characters (a-z, A-Z, 0-9) let rng = ChaCha20Rng::from_os_rng(); rng .sample_iter(Alphanumeric) .take(take) .map(char::from) .collect() } pub fn hash(string: &str) -> ApiResult { let salt = SaltString::generate(&mut OsRng); let hash = Argon2::default() .hash_password(string.as_bytes(), &salt)? .to_string(); Ok(hash) } pub fn verify_hash(string: &str, hashed_string: &str) -> bool { let bytes = string.as_bytes(); let parsed_hash = match PasswordHash::new(hashed_string) { Ok(h) => h, Err(err) => { log::error!( "Failed to construct PasswordHash from '{}': {}", hashed_string, err ); return false; } }; Argon2::default() .verify_password(bytes, &parsed_hash) .is_ok() } pub fn verify_role(auth: &Auth, role: &str) -> ApiResult<()> { if auth.user.role == role { Ok(()) } else { Err(Error { status: 403, details: "User does not have permission to perform this action.".to_string(), }) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_hash() { let password = hash("password").unwrap(); assert!(!verify_hash(&password, "bad_password")); assert!(verify_hash("password", &password)); } }