use crate::config_manager::{get_peachcloud_domain, load_peach_config, set_admin_password_hash, get_admin_password_hash, get_temporary_password_hash, set_temporary_password_hash}; use crate::error::PeachError; use crate::sbot_client; use log::info; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use std::iter; use crypto::digest::Digest; use crypto::sha3::Sha3; /// Returns Ok(()) if the supplied password is correct, /// and returns Err if the supplied password is incorrect. pub fn verify_password(password: &str) -> Result<(), PeachError> { let real_admin_password_hash = get_admin_password_hash()?; let password_hash = hash_password(&password.to_string()); if real_admin_password_hash == password_hash { Ok(()) } else { Err(PeachError::InvalidPassword) } } /// Checks if the given passwords are valid, and returns Ok() if they are and /// a PeachError otherwise. /// Currently this just checks that the passwords are the same, /// but could be extended to test if they are strong enough. pub fn validate_new_passwords(new_password1: &str, new_password2: &str) -> Result<(), PeachError> { if new_password1 == new_password2 { Ok(()) } else { Err(PeachError::PasswordsDoNotMatch) } } /// Sets a new password for the admin user pub fn set_new_password(new_password: &str) -> Result<(), PeachError> { let new_password_hash = hash_password(&new_password.to_string()); let result = set_admin_password_hash(&new_password_hash); match result { Ok(_) => { Ok(()) }, Err(_err) => { Err(PeachError::FailedToSetNewPassword { msg: "failed to save password hash".to_string() }) } } } /// Creates a hash from a password string pub fn hash_password(password: &str) -> String { let mut hasher = Sha3::sha3_256(); hasher.input_str(password); hasher.result_str() } /// Sets a new temporary password for the admin user /// which can be used to reset the permanent password pub fn set_new_temporary_password(new_password: &str) -> Result<(), PeachError> { let new_password_hash = hash_password(&new_password.to_string()); let result = set_temporary_password_hash(&new_password_hash); match result { Ok(_) => { Ok(()) }, Err(_err) => { Err(PeachError::FailedToSetNewPassword { msg: "failed to save temporary password hash".to_string() }) } } } /// Returns Ok(()) if the supplied temp_password is correct, /// and returns Err if the supplied temp_password is incorrect pub fn verify_temporary_password(password: &str) -> Result<(), PeachError> { let temporary_admin_password_hash = get_temporary_password_hash()?; let password_hash = hash_password(&password.to_string()); if temporary_admin_password_hash == password_hash { Ok(()) } else { Err(PeachError::InvalidPassword) } } /// Generates a temporary password and sends it via ssb dm /// to the ssb id configured to be the admin of the peachcloud device pub fn send_password_reset() -> Result<(), PeachError> { // first generate a new random password of ascii characters let mut rng = thread_rng(); let temporary_password: String = iter::repeat(()) .map(|()| rng.sample(Alphanumeric)) .map(char::from) .take(10) .collect(); // save this string as a new temporary password set_new_temporary_password(&temporary_password)?; let domain = get_peachcloud_domain()?; // then send temporary password as a private ssb message to admin let mut msg = format!( "Your new temporary password is: {} If you are on the same WiFi network as your PeachCloud device you can reset your password \ using this link: http://peach.local/reset_password", temporary_password ); // if there is an external domain, then include remote link in message // otherwise dont include it let remote_link = match domain { Some(domain) => { format!( "\n\nOr if you are on a different WiFi network, you can reset your password \ using the the following link: {}/reset_password", domain ) } None => "".to_string(), }; msg += &remote_link; // finally send the message to the admins let peach_config = load_peach_config()?; for ssb_admin_id in peach_config.ssb_admin_ids { sbot_client::private_message(&msg, &ssb_admin_id)?; } Ok(()) }