From 91180ed1fee4071c471e2afbf59e1d37b6590def Mon Sep 17 00:00:00 2001 From: glyph Date: Thu, 25 Nov 2021 09:55:56 +0200 Subject: [PATCH 1/8] clippy tidy-up --- peach-lib/src/lib.rs | 4 ---- peach-lib/src/network_client.rs | 20 ++++++++------------ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/peach-lib/src/lib.rs b/peach-lib/src/lib.rs index 2c68442..1144fc7 100644 --- a/peach-lib/src/lib.rs +++ b/peach-lib/src/lib.rs @@ -1,7 +1,3 @@ -// this is to ignore a clippy warning that suggests -// to replace code with the same code that is already there (possibly a bug) -#![allow(clippy::nonstandard_macro_braces)] - pub mod config_manager; pub mod dyndns_client; pub mod error; diff --git a/peach-lib/src/network_client.rs b/peach-lib/src/network_client.rs index cf22f2f..be03aa4 100644 --- a/peach-lib/src/network_client.rs +++ b/peach-lib/src/network_client.rs @@ -9,9 +9,6 @@ //! Several helper methods are also included here which bundle multiple client //! calls to achieve the desired functionality. -// TODO: fix these clippy errors so this allow can be removed -#![allow(clippy::needless_borrow)] - use std::env; use jsonrpc_client_core::{expand_params, jsonrpc_client}; @@ -166,9 +163,9 @@ pub fn disable(iface: &str, ssid: &str) -> std::result::Result std::result::Result std::result::Result { // retrieve a list of access points with saved credentials let saved_aps = match client.saved_networks().call() { Ok(ssids) => { - let networks: Vec = serde_json::from_str(ssids.as_str()) - .expect("Failed to deserialize saved_networks response"); + let networks: Vec = serde_json::from_str(ssids.as_str())?; networks } // return an empty vector if there are no saved access point credentials @@ -479,7 +475,7 @@ pub fn traffic(iface: &str) -> std::result::Result { let mut client = PeachNetworkClient::new(transport_handle); let response = client.traffic(iface).call()?; - let t: Traffic = serde_json::from_str(&response).unwrap(); + let t: Traffic = serde_json::from_str(&response)?; Ok(t) } @@ -506,13 +502,13 @@ pub fn update(iface: &str, ssid: &str, pass: &str) -> std::result::Result Date: Thu, 25 Nov 2021 09:56:10 +0200 Subject: [PATCH 2/8] remove snafu --- peach-lib/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/peach-lib/Cargo.toml b/peach-lib/Cargo.toml index e7e77b3..8fb44d1 100644 --- a/peach-lib/Cargo.toml +++ b/peach-lib/Cargo.toml @@ -4,8 +4,6 @@ version = "1.2.15" authors = ["Andrew Reid "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] log = "0.4" jsonrpc-client-core = "0.5" @@ -13,11 +11,9 @@ jsonrpc-client-http = "0.5" jsonrpc-core = "8.0.1" serde = { version = "1.0", features = ["derive"] } rust-crypto = "0.2.36" -serde_derive = "1.0" serde_json = "1.0" serde_yaml = "0.8" env_logger = "0.6" -snafu = "0.6" regex = "1" chrono = "0.4.19" rand="0.8.4" -- 2.40.1 From b2f7747357fbd82accfa45a96a581509d5004804 Mon Sep 17 00:00:00 2001 From: glyph Date: Thu, 25 Nov 2021 09:56:21 +0200 Subject: [PATCH 3/8] implement custom error type --- peach-lib/src/error.rs | 296 ++++++++++++++++++++++++++--------------- 1 file changed, 191 insertions(+), 105 deletions(-) diff --git a/peach-lib/src/error.rs b/peach-lib/src/error.rs index 96e7b4b..e309195 100644 --- a/peach-lib/src/error.rs +++ b/peach-lib/src/error.rs @@ -1,136 +1,222 @@ -//! Basic error handling for the network, OLED, stats and dyndns JSON-RPC clients. -pub use snafu::ResultExt; -use snafu::Snafu; -use std::error; -pub type BoxError = Box; +#![warn(missing_docs)] -#[derive(Debug, Snafu)] -#[snafu(visibility(pub(crate)))] +//! Error handling for various aspects of the PeachCloud system, including the network, OLED, stats and dyndns JSON-RPC clients, as well as the configuration manager, sbot client and password utilities. + +use std::{io, str, string}; + +/// This type represents all possible errors that can occur when interacting with the PeachCloud library. +#[derive(Debug)] pub enum PeachError { - #[snafu(display("{}", source))] - JsonRpcHttp { source: jsonrpc_client_http::Error }, - #[snafu(display("{}", source))] - JsonRpcClientCore { source: jsonrpc_client_core::Error }, - #[snafu(display("{}", source))] - Serde { source: serde_json::error::Error }, - #[snafu(display("{}", source))] - PeachParseBoolError { source: std::str::ParseBoolError }, - #[snafu(display("{}", source))] - SetConfigError { source: serde_yaml::Error }, - #[snafu(display("Failed to read: {}", file))] - ReadConfigError { - source: std::io::Error, - file: String, - }, - #[snafu(display("Failed to save: {}", file))] - WriteConfigError { - source: std::io::Error, - file: String, - }, - #[snafu(display("Failed to save tsig key: {} {}", path, source))] - SaveTsigKeyError { - source: std::io::Error, - path: String, - }, - #[snafu(display("{}", msg))] - NsUpdateError { msg: String }, - #[snafu(display("Failed to run nsupdate: {}", source))] - NsCommandError { source: std::io::Error }, - #[snafu(display("Failed to get public IP address: {}", source))] - GetPublicIpError { source: std::io::Error }, - #[snafu(display("Failed to decode public ip: {}", source))] - DecodePublicIpError { source: std::str::Utf8Error }, - #[snafu(display("Failed to decode nsupdate output: {}", source))] - DecodeNsUpdateOutputError { source: std::string::FromUtf8Error }, - #[snafu(display("{}", source))] - YamlError { source: serde_yaml::Error }, - #[snafu(display("{:?}", err))] - JsonRpcCore { err: jsonrpc_core::Error }, - #[snafu(display("Error creating regex: {}", source))] - RegexError { source: regex::Error }, - #[snafu(display("Failed to decode utf8: {}", source))] - FromUtf8Error { source: std::string::FromUtf8Error }, - #[snafu(display("Encountered Utf8Error: {}", source))] - Utf8Error { source: std::str::Utf8Error }, - #[snafu(display("Stdio error: {}: {}", msg, source))] - StdIoError { source: std::io::Error, msg: String }, - #[snafu(display("Failed to parse time from {} {}", source, msg))] - ChronoParseError { - source: chrono::ParseError, + /// Represents all other cases of `std::io::Error`. + Io(io::Error), + + /// Represents a JSON-RPC core error returned from a JSON-RPC client. + JsonRpcClientCore(jsonrpc_client_core::Error), + + /// Represents a JSON-RPC core error returned from a JSON-RPC server. + JsonRpcCore(jsonrpc_core::Error), + + /// Represents a JSON-RPC HTTP error returned from a JSON-RPC client. + JsonRpcHttp(jsonrpc_client_http::Error), + + /// Represents a failure to update the nameserver. + NsUpdate { + /// A message describing the context of the attempted nameserver update. msg: String, }, - #[snafu(display("Failed to save dynamic dns success log: {}", source))] - SaveDynDnsResultError { source: std::io::Error }, - #[snafu(display("New passwords do not match"))] - PasswordsDoNotMatch, - #[snafu(display("No admin password is set"))] + + /// Represents a failure to parse a string slice to a boolean value. + ParseBool(str::ParseBoolError), + + /// Represents a failure to parse a `DateTime`. Includes the error source and the file path + /// used in the parse attempt. + ParseDateTime { + /// The underlying source of the error. + source: chrono::ParseError, + /// The file path for the parse attempt. + path: String, + }, + + /// Represents the submission of an incorrect admin password. + PasswordIncorrect, + + /// Represents the submission of two passwords which do not match. + PasswordMismatch, + + /// Represents an unset admin password (empty password hash value) in the config file. PasswordNotSet, - #[snafu(display("The supplied password was not correct"))] - InvalidPassword, - #[snafu(display("Error saving new password: {}", msg))] - FailedToSetNewPassword { msg: String }, - #[snafu(display("Error calling sbotcli: {}", msg))] - SbotCliError { msg: String }, - #[snafu(display("Error deleting ssb admin id, id not found"))] - SsbAdminIdNotFound { id: String }, + + /// Represents a failure to read from input. Includes the error source and the file path used + /// in the read attempt. + Read { + /// The underlying source of the error. + source: io::Error, + /// The file path for the read attempt. + path: String, + }, + + /// Represents a failure to parse or compile a regular expression. + Regex(regex::Error), + + /// Represents a failure to successfully execute an sbot command. + SbotCli { + /// The `stderr` output from the sbot command. + msg: String, + }, + + /// Represents a failure to serialize or deserialize JSON. + SerdeJson(serde_json::error::Error), + + /// Represents a failure to serialize or deserialize YAML. + SerdeYaml(serde_yaml::Error), + + /// Represents a failure to find the given SSB ID in the config file. + SsbAdminIdNotFound { + /// An SSB ID (public key). + id: String, + }, + + /// Represents a failure to interpret a sequence of u8 as a string slice. + Utf8ToStr(str::Utf8Error), + + /// Represents a failure to interpret a sequence of u8 as a String. + Utf8ToString(string::FromUtf8Error), + + /// Represents a failure to write to output. Includes the error source and the file path used + /// in the write attempt. + Write { + /// The underlying source of the error. + source: io::Error, + /// The file path for the write attemp. + path: String, + }, } -impl From for PeachError { - fn from(err: jsonrpc_client_http::Error) -> PeachError { - PeachError::JsonRpcHttp { source: err } +impl std::error::Error for PeachError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + PeachError::Io(_) => None, + PeachError::JsonRpcClientCore(_) => None, + PeachError::JsonRpcCore(_) => None, + PeachError::JsonRpcHttp(_) => None, + PeachError::NsUpdate { .. } => None, + PeachError::ParseBool(_) => None, + PeachError::ParseDateTime { ref source, .. } => Some(source), + PeachError::PasswordIncorrect => None, + PeachError::PasswordMismatch => None, + PeachError::PasswordNotSet => None, + PeachError::Read { ref source, .. } => Some(source), + PeachError::Regex(_) => None, + PeachError::SbotCli { .. } => None, + PeachError::SerdeJson(_) => None, + PeachError::SerdeYaml(_) => None, + PeachError::SsbAdminIdNotFound { .. } => None, + PeachError::Utf8ToStr(_) => None, + PeachError::Utf8ToString(_) => None, + PeachError::Write { ref source, .. } => Some(source), + } } } -impl From for PeachError { - fn from(err: jsonrpc_client_core::Error) -> PeachError { - PeachError::JsonRpcClientCore { source: err } - } -} - -impl From for PeachError { - fn from(err: serde_json::error::Error) -> PeachError { - PeachError::Serde { source: err } - } -} - -impl From for PeachError { - fn from(err: serde_yaml::Error) -> PeachError { - PeachError::YamlError { source: err } +impl std::fmt::Display for PeachError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match *self { + PeachError::Io(ref err) => err.fmt(f), + PeachError::JsonRpcClientCore(ref err) => err.fmt(f), + PeachError::JsonRpcCore(ref err) => { + write!(f, "{:?}", err) + } + PeachError::JsonRpcHttp(ref err) => err.fmt(f), + PeachError::NsUpdate { ref msg } => { + write!(f, "Nameserver error: {}", msg) + } + PeachError::ParseBool(ref err) => err.fmt(f), + PeachError::ParseDateTime { ref path, .. } => { + write!(f, "Date/time parse error: {}", path) + } + PeachError::PasswordIncorrect => { + write!(f, "Password error: user-supplied password is incorrect") + } + PeachError::PasswordMismatch => { + write!(f, "Password error: user-supplied passwords do not match") + } + PeachError::PasswordNotSet => { + write!( + f, + "Password error: hash value in YAML configuration file is empty" + ) + } + PeachError::Read { ref path, .. } => { + write!(f, "Read error: {}", path) + } + PeachError::Regex(ref err) => err.fmt(f), + PeachError::SbotCli { ref msg } => { + write!(f, "Sbot error: {}", msg) + } + PeachError::SerdeJson(ref err) => err.fmt(f), + PeachError::SerdeYaml(ref err) => err.fmt(f), + PeachError::SsbAdminIdNotFound { ref id } => { + write!(f, "Config error: SSB admin ID `{}` not found", id) + } + PeachError::Utf8ToStr(ref err) => err.fmt(f), + PeachError::Utf8ToString(ref err) => err.fmt(f), + PeachError::Write { ref path, .. } => { + write!(f, "Write error: {}", path) + } + } } } impl From for PeachError { fn from(err: std::io::Error) -> PeachError { - PeachError::StdIoError { - source: err, - msg: "".to_string(), - } + PeachError::Io(err) + } +} + +impl From for PeachError { + fn from(err: jsonrpc_client_core::Error) -> PeachError { + PeachError::JsonRpcClientCore(err) + } +} + +impl From for PeachError { + fn from(err: jsonrpc_client_http::Error) -> PeachError { + PeachError::JsonRpcHttp(err) + } +} + +impl From for PeachError { + fn from(err: str::ParseBoolError) -> PeachError { + PeachError::ParseBool(err) } } impl From for PeachError { fn from(err: regex::Error) -> PeachError { - PeachError::RegexError { source: err } + PeachError::Regex(err) } } -impl From for PeachError { - fn from(err: std::string::FromUtf8Error) -> PeachError { - PeachError::FromUtf8Error { source: err } +impl From for PeachError { + fn from(err: serde_json::error::Error) -> PeachError { + PeachError::SerdeJson(err) } } -impl From for PeachError { - fn from(err: std::str::Utf8Error) -> PeachError { - PeachError::Utf8Error { source: err } +impl From for PeachError { + fn from(err: serde_yaml::Error) -> PeachError { + PeachError::SerdeYaml(err) } } -impl From for PeachError { - fn from(err: chrono::ParseError) -> PeachError { - PeachError::ChronoParseError { - source: err, - msg: "".to_string(), - } +impl From for PeachError { + fn from(err: str::Utf8Error) -> PeachError { + PeachError::Utf8ToStr(err) + } +} + +impl From for PeachError { + fn from(err: string::FromUtf8Error) -> PeachError { + PeachError::Utf8ToString(err) } } -- 2.40.1 From e6a6fcdc891bc27e15df478f846f4268fd069eaf Mon Sep 17 00:00:00 2001 From: glyph Date: Thu, 25 Nov 2021 09:58:00 +0200 Subject: [PATCH 4/8] remove snafu context and improve error handling --- peach-lib/src/config_manager.rs | 18 ++-- peach-lib/src/dyndns_client.rs | 145 ++++++++++++++++---------------- peach-lib/src/password_utils.rs | 52 ++++-------- peach-lib/src/sbot_client.rs | 14 +-- 4 files changed, 108 insertions(+), 121 deletions(-) diff --git a/peach-lib/src/config_manager.rs b/peach-lib/src/config_manager.rs index 2581408..fd31a6b 100644 --- a/peach-lib/src/config_manager.rs +++ b/peach-lib/src/config_manager.rs @@ -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 Result { } // 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 { } else { Err(PeachError::PasswordNotSet) } -} \ No newline at end of file +} diff --git a/peach-lib/src/dyndns_client.rs b/peach-lib/src/dyndns_client.rs index df58947..ed7eec7 100644 --- a/peach-lib/src/dyndns_client.rs +++ b/peach-lib/src/dyndns_client.rs @@ -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 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 { - let result: Result = 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 { // 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 { 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 { 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 { 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 { 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, 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::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 { // 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` 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 diff --git a/peach-lib/src/password_utils.rs b/peach-lib/src/password_utils.rs index f60b861..4223ff2 100644 --- a/peach-lib/src/password_utils.rs +++ b/peach-lib/src/password_utils.rs @@ -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)?; } diff --git a/peach-lib/src/sbot_client.rs b/peach-lib/src/sbot_client.rs index 86dbc3f..1e28e87 100644 --- a/peach-lib/src/sbot_client.rs +++ b/peach-lib/src/sbot_client.rs @@ -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 { 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), }) } -- 2.40.1 From bf9dd7f567939a42aaa639f2f51b63d72b322f60 Mon Sep 17 00:00:00 2001 From: glyph Date: Thu, 25 Nov 2021 09:58:30 +0200 Subject: [PATCH 5/8] bump version --- peach-lib/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peach-lib/Cargo.toml b/peach-lib/Cargo.toml index 8fb44d1..af5aa51 100644 --- a/peach-lib/Cargo.toml +++ b/peach-lib/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "peach-lib" -version = "1.2.15" -authors = ["Andrew Reid "] +version = "1.3.0" +authors = ["Andrew Reid "] edition = "2018" [dependencies] -- 2.40.1 From c72c57c3452cbdf27852954d58e67e7e248b0fc6 Mon Sep 17 00:00:00 2001 From: glyph Date: Thu, 25 Nov 2021 09:58:49 +0200 Subject: [PATCH 6/8] update lockfile --- Cargo.lock | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be13e3b..9ea32bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2584,10 +2584,8 @@ dependencies = [ "regex", "rust-crypto", "serde 1.0.130", - "serde_derive", "serde_json", "serde_yaml", - "snafu 0.6.10", ] [[package]] -- 2.40.1 From 406206bab3ae8c72bbb3ad3da9e67250a34d9abd Mon Sep 17 00:00:00 2001 From: glyph Date: Thu, 25 Nov 2021 12:20:11 +0200 Subject: [PATCH 7/8] update lockfile --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 9ea32bb..d7c1a12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2571,7 +2571,7 @@ dependencies = [ [[package]] name = "peach-lib" -version = "1.2.15" +version = "1.3.0" dependencies = [ "chrono", "env_logger 0.6.2", -- 2.40.1 From 30f00524f4d41ed6abf1255cefa11965fe46ff63 Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 30 Nov 2021 13:55:11 +0200 Subject: [PATCH 8/8] remove unneeded dependency --- peach-lib/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/peach-lib/Cargo.toml b/peach-lib/Cargo.toml index af5aa51..e1586d0 100644 --- a/peach-lib/Cargo.toml +++ b/peach-lib/Cargo.toml @@ -13,7 +13,6 @@ serde = { version = "1.0", features = ["derive"] } rust-crypto = "0.2.36" serde_json = "1.0" serde_yaml = "0.8" -env_logger = "0.6" regex = "1" chrono = "0.4.19" rand="0.8.4" -- 2.40.1