remove snafu context and improve error handling
This commit is contained in:
parent
b2f7747357
commit
e6a6fcdc89
|
@ -4,12 +4,12 @@
|
|||
//!
|
||||
//! The configuration file is located at: "/var/lib/peachcloud/config.yml"
|
||||
|
||||
use fslock::{LockFile};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
|
||||
use fslock::LockFile;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::PeachError;
|
||||
use crate::error::*;
|
||||
|
||||
// main configuration file
|
||||
pub const YAML_PATH: &str = "/var/lib/peachcloud/config.yml";
|
||||
|
@ -48,8 +48,9 @@ fn save_peach_config(peach_config: PeachConfig) -> Result<PeachConfig, PeachErro
|
|||
|
||||
let yaml_str = serde_yaml::to_string(&peach_config)?;
|
||||
|
||||
fs::write(YAML_PATH, yaml_str).context(WriteConfigError {
|
||||
file: YAML_PATH.to_string(),
|
||||
fs::write(YAML_PATH, yaml_str).map_err(|source| PeachError::Write {
|
||||
source,
|
||||
path: YAML_PATH.to_string(),
|
||||
})?;
|
||||
|
||||
// unlock file lock
|
||||
|
@ -79,8 +80,9 @@ pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
|
|||
}
|
||||
// otherwise we load peach config from disk
|
||||
else {
|
||||
let contents = fs::read_to_string(YAML_PATH).context(ReadConfigError {
|
||||
file: YAML_PATH.to_string(),
|
||||
let contents = fs::read_to_string(YAML_PATH).map_err(|source| PeachError::Read {
|
||||
source,
|
||||
path: YAML_PATH.to_string(),
|
||||
})?;
|
||||
peach_config = serde_yaml::from_str(&contents)?;
|
||||
}
|
||||
|
@ -176,4 +178,4 @@ pub fn get_temporary_password_hash() -> Result<String, PeachError> {
|
|||
} else {
|
||||
Err(PeachError::PasswordNotSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,24 +9,24 @@
|
|||
//!
|
||||
//! The domain for dyndns updates is stored in /var/lib/peachcloud/config.yml
|
||||
//! The tsig key for authenticating the updates is stored in /var/lib/peachcloud/peach-dyndns/tsig.key
|
||||
use crate::config_manager::{load_peach_config, set_peach_dyndns_config};
|
||||
use crate::error::PeachError;
|
||||
use crate::error::{
|
||||
ChronoParseError, DecodeNsUpdateOutputError, DecodePublicIpError, GetPublicIpError,
|
||||
NsCommandError, SaveDynDnsResultError, SaveTsigKeyError,
|
||||
use std::{
|
||||
fs,
|
||||
fs::OpenOptions,
|
||||
io::Write,
|
||||
process::{Command, Stdio},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use chrono::prelude::*;
|
||||
use jsonrpc_client_core::{expand_params, jsonrpc_client};
|
||||
use jsonrpc_client_http::HttpTransport;
|
||||
use log::{debug, info};
|
||||
use regex::Regex;
|
||||
use snafu::ResultExt;
|
||||
use std::fs;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::str::FromStr;
|
||||
use std::str::ParseBoolError;
|
||||
|
||||
use crate::{
|
||||
config_manager::{load_peach_config, set_peach_dyndns_config},
|
||||
error::PeachError,
|
||||
};
|
||||
|
||||
/// constants for dyndns configuration
|
||||
pub const PEACH_DYNDNS_URL: &str = "http://dynserver.dyn.peachcloud.org";
|
||||
|
@ -37,20 +37,21 @@ pub const DYNDNS_LOG_PATH: &str = "/var/lib/peachcloud/peach-dyndns/latest_resul
|
|||
/// helper function which saves dyndns TSIG key returned by peach-dyndns-server to /var/lib/peachcloud/peach-dyndns/tsig.key
|
||||
pub fn save_dyndns_key(key: &str) -> Result<(), PeachError> {
|
||||
// create directory if it doesn't exist
|
||||
fs::create_dir_all(PEACH_DYNDNS_CONFIG_PATH).context(SaveTsigKeyError {
|
||||
path: PEACH_DYNDNS_CONFIG_PATH.to_string(),
|
||||
})?;
|
||||
fs::create_dir_all(PEACH_DYNDNS_CONFIG_PATH)?;
|
||||
//.context(SaveTsigKeyError {
|
||||
//path: PEACH_DYNDNS_CONFIG_PATH.to_string(),
|
||||
//})?;
|
||||
// write key text
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(TSIG_KEY_PATH)
|
||||
.context(SaveTsigKeyError {
|
||||
path: TSIG_KEY_PATH.to_string(),
|
||||
})?;
|
||||
writeln!(file, "{}", key).context(SaveTsigKeyError {
|
||||
// TODO: consider adding context msg
|
||||
.open(TSIG_KEY_PATH)?;
|
||||
writeln!(file, "{}", key).map_err(|source| PeachError::Write {
|
||||
source,
|
||||
path: TSIG_KEY_PATH.to_string(),
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -68,23 +69,17 @@ pub fn register_domain(domain: &str) -> std::result::Result<String, PeachError>
|
|||
let mut client = PeachDynDnsClient::new(transport_handle);
|
||||
|
||||
info!("Performing register_domain call to peach-dyndns-server");
|
||||
let res = client.register_domain(domain).call();
|
||||
match res {
|
||||
Ok(key) => {
|
||||
// save new TSIG key
|
||||
save_dyndns_key(&key)?;
|
||||
// save new configuration values
|
||||
let set_config_result =
|
||||
set_peach_dyndns_config(domain, PEACH_DYNDNS_URL, TSIG_KEY_PATH, true);
|
||||
match set_config_result {
|
||||
Ok(_) => {
|
||||
let response = "success".to_string();
|
||||
Ok(response)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
let key = client.register_domain(domain).call()?;
|
||||
// save new TSIG key
|
||||
save_dyndns_key(&key)?;
|
||||
// save new configuration values
|
||||
let set_config_result = set_peach_dyndns_config(domain, PEACH_DYNDNS_URL, TSIG_KEY_PATH, true);
|
||||
match set_config_result {
|
||||
Ok(_) => {
|
||||
let response = "success".to_string();
|
||||
Ok(response)
|
||||
}
|
||||
Err(err) => Err(PeachError::JsonRpcClientCore { source: err }),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,29 +94,20 @@ pub fn is_domain_available(domain: &str) -> std::result::Result<bool, PeachError
|
|||
let mut client = PeachDynDnsClient::new(transport_handle);
|
||||
|
||||
info!("Performing register_domain call to peach-dyndns-server");
|
||||
let res = client.is_domain_available(domain).call();
|
||||
info!("res: {:?}", res);
|
||||
match res {
|
||||
Ok(result_str) => {
|
||||
let result: Result<bool, ParseBoolError> = FromStr::from_str(&result_str);
|
||||
match result {
|
||||
Ok(result_bool) => Ok(result_bool),
|
||||
Err(err) => Err(PeachError::PeachParseBoolError { source: err }),
|
||||
}
|
||||
}
|
||||
Err(err) => Err(PeachError::JsonRpcClientCore { source: err }),
|
||||
}
|
||||
let domain_availability = client.is_domain_available(domain).call()?;
|
||||
info!("Domain availability: {:?}", domain_availability);
|
||||
// convert availability status to a bool
|
||||
let available: bool = FromStr::from_str(&domain_availability)?;
|
||||
|
||||
Ok(available)
|
||||
}
|
||||
|
||||
/// Helper function to get public ip address of PeachCloud device.
|
||||
fn get_public_ip_address() -> Result<String, PeachError> {
|
||||
// TODO: consider other ways to get public IP address
|
||||
let output = Command::new("/usr/bin/curl")
|
||||
.arg("ifconfig.me")
|
||||
.output()
|
||||
.context(GetPublicIpError)?;
|
||||
let command_output = std::str::from_utf8(&output.stdout).context(DecodePublicIpError)?;
|
||||
Ok(command_output.to_string())
|
||||
let output = Command::new("/usr/bin/curl").arg("ifconfig.me").output()?;
|
||||
let command_output = String::from_utf8(output.stdout)?;
|
||||
Ok(command_output)
|
||||
}
|
||||
|
||||
/// Reads dyndns configurations from config.yml
|
||||
|
@ -146,13 +132,12 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
|||
Ok(false)
|
||||
} else {
|
||||
// call nsupdate passing appropriate configs
|
||||
let nsupdate_command = Command::new("/usr/bin/nsupdate")
|
||||
let mut nsupdate_command = Command::new("/usr/bin/nsupdate")
|
||||
.arg("-k")
|
||||
.arg(peach_config.dyn_tsig_key_path)
|
||||
.arg(&peach_config.dyn_tsig_key_path)
|
||||
.arg("-v")
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.context(NsCommandError)?;
|
||||
.spawn()?;
|
||||
// pass nsupdate commands via stdin
|
||||
let public_ip_address = get_public_ip_address()?;
|
||||
info!("found public ip address: {}", public_ip_address);
|
||||
|
@ -168,11 +153,15 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
|||
DOMAIN = peach_config.dyn_domain,
|
||||
PUBLIC_IP_ADDRESS = public_ip_address,
|
||||
);
|
||||
write!(nsupdate_command.stdin.as_ref().unwrap(), "{}", ns_commands).unwrap();
|
||||
let nsupdate_output = nsupdate_command
|
||||
.wait_with_output()
|
||||
.context(NsCommandError)?;
|
||||
info!("output: {:?}", nsupdate_output);
|
||||
let mut nsupdate_stdin = nsupdate_command.stdin.take().ok_or(PeachError::NsUpdate {
|
||||
msg: "unable to capture stdin handle for `nsupdate` command".to_string(),
|
||||
})?;
|
||||
write!(nsupdate_stdin, "{}", ns_commands).map_err(|source| PeachError::Write {
|
||||
source,
|
||||
path: peach_config.dyn_tsig_key_path.to_string(),
|
||||
})?;
|
||||
let nsupdate_output = nsupdate_command.wait_with_output()?;
|
||||
info!("nsupdate output: {:?}", nsupdate_output);
|
||||
// We only return a successful result if nsupdate was successful
|
||||
if nsupdate_output.status.success() {
|
||||
info!("nsupdate succeeded, returning ok");
|
||||
|
@ -182,9 +171,8 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
|
|||
Ok(true)
|
||||
} else {
|
||||
info!("nsupdate failed, returning error");
|
||||
let err_msg =
|
||||
String::from_utf8(nsupdate_output.stdout).context(DecodeNsUpdateOutputError)?;
|
||||
Err(PeachError::NsUpdateError { msg: err_msg })
|
||||
let err_msg = String::from_utf8(nsupdate_output.stdout)?;
|
||||
Err(PeachError::NsUpdate { msg: err_msg })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,9 +183,12 @@ pub fn log_successful_nsupdate() -> Result<bool, PeachError> {
|
|||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(DYNDNS_LOG_PATH)
|
||||
.context(SaveDynDnsResultError)?;
|
||||
write!(file, "{}", now_timestamp).context(SaveDynDnsResultError)?;
|
||||
// TODO: possibly add a context msg here ("failed to open dynamic dns success log")
|
||||
.open(DYNDNS_LOG_PATH)?;
|
||||
write!(file, "{}", now_timestamp).map_err(|source| PeachError::Write {
|
||||
source,
|
||||
path: DYNDNS_LOG_PATH.to_string(),
|
||||
})?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
@ -207,12 +198,19 @@ pub fn get_num_seconds_since_successful_dns_update() -> Result<Option<i64>, Peac
|
|||
if !log_exists {
|
||||
Ok(None)
|
||||
} else {
|
||||
let contents =
|
||||
fs::read_to_string(DYNDNS_LOG_PATH).expect("Something went wrong reading the file");
|
||||
let contents = fs::read_to_string(DYNDNS_LOG_PATH).map_err(|source| PeachError::Read {
|
||||
source,
|
||||
path: DYNDNS_LOG_PATH.to_string(),
|
||||
})?;
|
||||
// replace newline if found
|
||||
// TODO: maybe we can use `.trim()` instead
|
||||
let contents = contents.replace("\n", "");
|
||||
let time_ran_dt = DateTime::parse_from_rfc3339(&contents).context(ChronoParseError {
|
||||
msg: "Error parsing dyndns time from latest_result.log".to_string(),
|
||||
// TODO: consider adding additional context?
|
||||
let time_ran_dt = DateTime::parse_from_rfc3339(&contents).map_err(|source| {
|
||||
PeachError::ParseDateTime {
|
||||
source,
|
||||
path: DYNDNS_LOG_PATH.to_string(),
|
||||
}
|
||||
})?;
|
||||
let current_time: DateTime<Utc> = Utc::now();
|
||||
let duration = current_time.signed_duration_since(time_ran_dt);
|
||||
|
@ -261,6 +259,7 @@ pub fn get_dyndns_subdomain(dyndns_full_domain: &str) -> Option<String> {
|
|||
|
||||
// helper function which checks if a dyndns domain is new
|
||||
pub fn check_is_new_dyndns_domain(dyndns_full_domain: &str) -> bool {
|
||||
// TODO: return `Result<bool, PeachError>` and replace `unwrap` with `?` operator
|
||||
let peach_config = load_peach_config().unwrap();
|
||||
let previous_dyndns_domain = peach_config.dyn_domain;
|
||||
dyndns_full_domain != previous_dyndns_domain
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
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 rand::distributions::Alphanumeric;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::iter;
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha3::Sha3;
|
||||
|
||||
use crypto::{digest::Digest, sha3::Sha3};
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
|
||||
use crate::{config_manager, error::PeachError, sbot_client};
|
||||
|
||||
/// 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 real_admin_password_hash = config_manager::get_admin_password_hash()?;
|
||||
let password_hash = hash_password(&password.to_string());
|
||||
if real_admin_password_hash == password_hash {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PeachError::InvalidPassword)
|
||||
Err(PeachError::PasswordIncorrect)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,22 +25,16 @@ pub fn validate_new_passwords(new_password1: &str, new_password2: &str) -> Resul
|
|||
if new_password1 == new_password2 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PeachError::PasswordsDoNotMatch)
|
||||
Err(PeachError::PasswordMismatch)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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() })
|
||||
}
|
||||
}
|
||||
config_manager::set_admin_password_hash(&new_password_hash)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a hash from a password string
|
||||
|
@ -58,26 +48,20 @@ pub fn hash_password(password: &str) -> String {
|
|||
/// 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() })
|
||||
}
|
||||
}
|
||||
config_manager::set_temporary_password_hash(&new_password_hash)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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 temporary_admin_password_hash = config_manager::get_temporary_password_hash()?;
|
||||
let password_hash = hash_password(&password.to_string());
|
||||
if temporary_admin_password_hash == password_hash {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PeachError::InvalidPassword)
|
||||
Err(PeachError::PasswordIncorrect)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +77,7 @@ pub fn send_password_reset() -> Result<(), PeachError> {
|
|||
.collect();
|
||||
// save this string as a new temporary password
|
||||
set_new_temporary_password(&temporary_password)?;
|
||||
let domain = get_peachcloud_domain()?;
|
||||
let domain = config_manager::get_peachcloud_domain()?;
|
||||
|
||||
// then send temporary password as a private ssb message to admin
|
||||
let mut msg = format!(
|
||||
|
@ -117,7 +101,7 @@ using this link: http://peach.local/reset_password",
|
|||
};
|
||||
msg += &remote_link;
|
||||
// finally send the message to the admins
|
||||
let peach_config = load_peach_config()?;
|
||||
let peach_config = config_manager::load_peach_config()?;
|
||||
for ssb_admin_id in peach_config.ssb_admin_ids {
|
||||
sbot_client::private_message(&msg, &ssb_admin_id)?;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
//! Interfaces for monitoring and configuring go-sbot using sbotcli.
|
||||
//!
|
||||
use crate::error::PeachError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::PeachError;
|
||||
|
||||
pub fn is_sbot_online() -> Result<bool, PeachError> {
|
||||
let output = Command::new("/usr/bin/systemctl")
|
||||
.arg("status")
|
||||
|
@ -36,7 +38,7 @@ pub fn post(msg: &str) -> Result<(), PeachError> {
|
|||
Ok(())
|
||||
} else {
|
||||
let stderr = std::str::from_utf8(&output.stderr)?;
|
||||
Err(PeachError::SbotCliError {
|
||||
Err(PeachError::SbotCli {
|
||||
msg: format!("Error making ssb post: {}", stderr),
|
||||
})
|
||||
}
|
||||
|
@ -83,7 +85,7 @@ pub fn update_pub_name(new_name: &str) -> Result<(), PeachError> {
|
|||
Ok(())
|
||||
} else {
|
||||
let stderr = std::str::from_utf8(&output.stderr)?;
|
||||
Err(PeachError::SbotCliError {
|
||||
Err(PeachError::SbotCli {
|
||||
msg: format!("Error updating pub name: {}", stderr),
|
||||
})
|
||||
}
|
||||
|
@ -102,7 +104,7 @@ pub fn private_message(msg: &str, recipient: &str) -> Result<(), PeachError> {
|
|||
Ok(())
|
||||
} else {
|
||||
let stderr = std::str::from_utf8(&output.stderr)?;
|
||||
Err(PeachError::SbotCliError {
|
||||
Err(PeachError::SbotCli {
|
||||
msg: format!("Error sending ssb private message: {}", stderr),
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue