Error refactor for peach-lib #38
|
@ -2571,7 +2571,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "peach-lib"
|
||||
version = "1.2.15"
|
||||
version = "1.3.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"env_logger 0.6.2",
|
||||
|
@ -2584,10 +2584,8 @@ dependencies = [
|
|||
"regex",
|
||||
"rust-crypto",
|
||||
"serde 1.0.130",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"snafu 0.6.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
[package]
|
||||
name = "peach-lib"
|
||||
version = "1.2.15"
|
||||
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
|
||||
version = "1.3.0"
|
||||
authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
||||
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,8 @@ 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"
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
@ -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,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<dyn error::Error>;
|
||||
#![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<jsonrpc_client_http::Error> 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<jsonrpc_client_core::Error> for PeachError {
|
||||
fn from(err: jsonrpc_client_core::Error) -> PeachError {
|
||||
PeachError::JsonRpcClientCore { source: err }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::error::Error> for PeachError {
|
||||
fn from(err: serde_json::error::Error) -> PeachError {
|
||||
PeachError::Serde { source: err }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_yaml::Error> 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<std::io::Error> for PeachError {
|
||||
fn from(err: std::io::Error) -> PeachError {
|
||||
PeachError::StdIoError {
|
||||
source: err,
|
||||
msg: "".to_string(),
|
||||
}
|
||||
PeachError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<jsonrpc_client_core::Error> for PeachError {
|
||||
fn from(err: jsonrpc_client_core::Error) -> PeachError {
|
||||
PeachError::JsonRpcClientCore(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<jsonrpc_client_http::Error> for PeachError {
|
||||
fn from(err: jsonrpc_client_http::Error) -> PeachError {
|
||||
PeachError::JsonRpcHttp(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<str::ParseBoolError> for PeachError {
|
||||
fn from(err: str::ParseBoolError) -> PeachError {
|
||||
PeachError::ParseBool(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<regex::Error> for PeachError {
|
||||
fn from(err: regex::Error) -> PeachError {
|
||||
PeachError::RegexError { source: err }
|
||||
PeachError::Regex(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for PeachError {
|
||||
fn from(err: std::string::FromUtf8Error) -> PeachError {
|
||||
PeachError::FromUtf8Error { source: err }
|
||||
impl From<serde_json::error::Error> for PeachError {
|
||||
fn from(err: serde_json::error::Error) -> PeachError {
|
||||
PeachError::SerdeJson(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::str::Utf8Error> for PeachError {
|
||||
fn from(err: std::str::Utf8Error) -> PeachError {
|
||||
PeachError::Utf8Error { source: err }
|
||||
impl From<serde_yaml::Error> for PeachError {
|
||||
fn from(err: serde_yaml::Error) -> PeachError {
|
||||
PeachError::SerdeYaml(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<chrono::ParseError> for PeachError {
|
||||
fn from(err: chrono::ParseError) -> PeachError {
|
||||
PeachError::ChronoParseError {
|
||||
source: err,
|
||||
msg: "".to_string(),
|
||||
}
|
||||
impl From<str::Utf8Error> for PeachError {
|
||||
fn from(err: str::Utf8Error) -> PeachError {
|
||||
PeachError::Utf8ToStr(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<string::FromUtf8Error> for PeachError {
|
||||
fn from(err: string::FromUtf8Error) -> PeachError {
|
||||
PeachError::Utf8ToString(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<String, PeachErro
|
|||
let mut client = PeachNetworkClient::new(transport_handle);
|
||||
|
||||
info!("Performing id call to peach-network microservice.");
|
||||
let id = client.id(&iface, &ssid).call()?;
|
||||
let id = client.id(iface, ssid).call()?;
|
||||
info!("Performing disable call to peach-network microservice.");
|
||||
client.disable(&id, &iface).call()?;
|
||||
client.disable(&id, iface).call()?;
|
||||
|
||||
let response = "success".to_string();
|
||||
|
||||
|
@ -194,12 +191,12 @@ pub fn forget(iface: &str, ssid: &str) -> std::result::Result<String, PeachError
|
|||
let mut client = PeachNetworkClient::new(transport_handle);
|
||||
|
||||
info!("Performing id call to peach-network microservice.");
|
||||
let id = client.id(&iface, &ssid).call()?;
|
||||
let id = client.id(iface, ssid).call()?;
|
||||
info!("Performing delete call to peach-network microservice.");
|
||||
// WEIRD BUG: the parameters below are technically in the wrong order:
|
||||
// it should be id first and then iface, but somehow they get twisted.
|
||||
// i don't understand computers.
|
||||
client.delete(&iface, &id).call()?;
|
||||
client.delete(iface, &id).call()?;
|
||||
info!("Performing save call to peach-network microservice.");
|
||||
client.save().call()?;
|
||||
|
||||
|
@ -357,8 +354,7 @@ pub fn saved_ap(ssid: &str) -> std::result::Result<bool, PeachError> {
|
|||
// retrieve a list of access points with saved credentials
|
||||
let saved_aps = match client.saved_networks().call() {
|
||||
Ok(ssids) => {
|
||||
let networks: Vec<Networks> = serde_json::from_str(ssids.as_str())
|
||||
.expect("Failed to deserialize saved_networks response");
|
||||
let networks: Vec<Networks> = 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<Traffic, PeachError> {
|
|||
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<String
|
|||
|
||||
// get the id of the network
|
||||
info!("Performing id call to peach-network microservice.");
|
||||
let id = client.id(&iface, &ssid).call()?;
|
||||
let id = client.id(iface, ssid).call()?;
|
||||
// delete the old credentials
|
||||
// WEIRD BUG: the parameters below are technically in the wrong order:
|
||||
// it should be id first and then iface, but somehow they get twisted.
|
||||
// i don't understand computers.
|
||||
info!("Performing delete call to peach-network microservice.");
|
||||
client.delete(&iface, &id).call()?;
|
||||
client.delete(iface, &id).call()?;
|
||||
// save the updates to wpa_supplicant.conf
|
||||
info!("Performing save call to peach-network microservice.");
|
||||
client.save().call()?;
|
||||
|
|
|
@ -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