mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-05 20:40:02 -08:00
chore(doc): Docs for rosenpass::{config, cli} (#560)
This commit is contained in:
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::app_server::AppServer;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
|
||||
pub struct ApiConfig {
|
||||
/// Where in the file-system to create the unix socket the rosenpass API will be listening for
|
||||
/// connections on
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
//! Contains the code used to parse command line parameters for rosenpass.
|
||||
//!
|
||||
//! [CliArgs::run] is called by the rosenpass main function and contains the
|
||||
//! bulk of our boostrapping code while the main function just sets up the basic environment
|
||||
|
||||
use anyhow::{bail, ensure, Context};
|
||||
use clap::{Parser, Subcommand};
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
@@ -31,15 +36,25 @@ use {
|
||||
std::thread,
|
||||
};
|
||||
|
||||
/// enum representing a choice of interface to a WireGuard broker
|
||||
/// How to reach a WireGuard PSK Broker
|
||||
#[derive(Debug)]
|
||||
pub enum BrokerInterface {
|
||||
/// The PSK Broker is listening on a unix socket at the given path
|
||||
Socket(PathBuf),
|
||||
/// The PSK Broker broker is already connected to this process; a
|
||||
/// unix socket stream can be reached at the given file descriptor.
|
||||
///
|
||||
/// This is generally used with file descriptor passing.
|
||||
FileDescriptor(i32),
|
||||
/// Create a socketpair(3p), spawn the PSK broker process from within rosenpass,
|
||||
/// and hand one end of the socketpair to the broker process via file
|
||||
/// descriptor passing to the subprocess
|
||||
SocketPair,
|
||||
}
|
||||
|
||||
/// struct holding all CLI arguments for `clap` crate to parse
|
||||
/// Command line arguments to the Rosenpass binary.
|
||||
///
|
||||
/// Used for parsing with [clap].
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about, arg_required_else_help = true)]
|
||||
pub struct CliArgs {
|
||||
@@ -80,6 +95,7 @@ pub struct CliArgs {
|
||||
#[arg(short, long, group = "psk-broker-specs")]
|
||||
psk_broker_spawn: bool,
|
||||
|
||||
/// The subcommand to be invoked
|
||||
#[command(subcommand)]
|
||||
pub command: Option<CliCommand>,
|
||||
|
||||
@@ -98,6 +114,10 @@ pub struct CliArgs {
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
/// Apply the command line parameters to the Rosenpass configuration struct
|
||||
///
|
||||
/// Generally the flow of control here is that all the command line parameters
|
||||
/// are merged into the configuration file to avoid much code duplication.
|
||||
pub fn apply_to_config(&self, _cfg: &mut config::Rosenpass) -> anyhow::Result<()> {
|
||||
#[cfg(feature = "experiment_api")]
|
||||
self.api.apply_to_config(_cfg)?;
|
||||
@@ -123,9 +143,11 @@ impl CliArgs {
|
||||
None
|
||||
}
|
||||
|
||||
/// Return the WireGuard PSK broker interface configured.
|
||||
///
|
||||
/// Returns `None` if the `experiment_api` feature is disabled.
|
||||
|
||||
#[cfg(feature = "experiment_api")]
|
||||
/// returns the broker interface set by CLI args
|
||||
/// returns `None` if the `experiment_api` feature isn't enabled
|
||||
pub fn get_broker_interface(&self) -> Option<BrokerInterface> {
|
||||
if let Some(path_ref) = self.psk_broker_path.as_ref() {
|
||||
Some(BrokerInterface::Socket(path_ref.to_path_buf()))
|
||||
@@ -138,9 +160,10 @@ impl CliArgs {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the WireGuard PSK broker interface configured.
|
||||
///
|
||||
/// Returns `None` if the `experiment_api` feature is disabled.
|
||||
#[cfg(not(feature = "experiment_api"))]
|
||||
/// returns the broker interface set by CLI args
|
||||
/// returns `None` if the `experiment_api` feature isn't enabled
|
||||
pub fn get_broker_interface(&self) -> Option<BrokerInterface> {
|
||||
None
|
||||
}
|
||||
@@ -244,15 +267,17 @@ pub enum CliCommand {
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
/// Runs the command specified via CLI
|
||||
/// Run Rosenpass with the given command line parameters
|
||||
///
|
||||
/// ## TODO
|
||||
/// - This method consumes the [`CliCommand`] value. It might be wise to use a reference...
|
||||
/// This contains the bulk of our startup logic with
|
||||
/// the main function just setting up the basic environment
|
||||
/// and then calling this function.
|
||||
pub fn run(
|
||||
self,
|
||||
broker_interface: Option<BrokerInterface>,
|
||||
test_helpers: Option<AppServerTest>,
|
||||
) -> anyhow::Result<()> {
|
||||
// TODO: This method consumes the [`CliCommand`] value. It might be wise to use a reference...
|
||||
use CliCommand::*;
|
||||
match &self.command {
|
||||
Some(GenConfig { config_file, force }) => {
|
||||
@@ -403,6 +428,7 @@ impl CliArgs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Used by [Self::run] to start the Rosenpass key exchange server
|
||||
fn event_loop(
|
||||
config: config::Rosenpass,
|
||||
broker_interface: Option<BrokerInterface>,
|
||||
@@ -470,6 +496,19 @@ impl CliArgs {
|
||||
srv.event_loop()
|
||||
}
|
||||
|
||||
/// Create the WireGuard PSK broker to be used by
|
||||
/// [crate::app_server::AppServer].
|
||||
///
|
||||
/// If the `experiment_api`
|
||||
/// feature flag is set, then this communicates with a PSK broker
|
||||
/// running in a different process as configured via
|
||||
/// the `psk_broker_path`, `psk_broker_fd`, and `psk_broker_spawn`
|
||||
/// fields.
|
||||
///
|
||||
/// If the `experiment_api`
|
||||
/// feature flag is not set, then this returns a [NativeUnixBroker],
|
||||
/// sending pre-shared keys directly to WireGuard from within this
|
||||
/// process.
|
||||
#[cfg(feature = "experiment_api")]
|
||||
fn create_broker(
|
||||
broker_interface: Option<BrokerInterface>,
|
||||
@@ -485,6 +524,19 @@ impl CliArgs {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the WireGuard PSK broker to be used by
|
||||
/// [crate::app_server::AppServer].
|
||||
///
|
||||
/// If the `experiment_api`
|
||||
/// feature flag is set, then this communicates with a PSK broker
|
||||
/// running in a different process as configured via
|
||||
/// the `psk_broker_path`, `psk_broker_fd`, and `psk_broker_spawn`
|
||||
/// fields.
|
||||
///
|
||||
/// If the `experiment_api`
|
||||
/// feature flag is not set, then this returns a [NativeUnixBroker],
|
||||
/// sending pre-shared keys directly to WireGuard from within this
|
||||
/// process.
|
||||
#[cfg(not(feature = "experiment_api"))]
|
||||
fn create_broker(
|
||||
_broker_interface: Option<BrokerInterface>,
|
||||
@@ -492,6 +544,10 @@ impl CliArgs {
|
||||
Ok(Box::new(NativeUnixBroker::new()))
|
||||
}
|
||||
|
||||
/// Used by [Self::create_broker] if the `experiment_api` is configured
|
||||
/// to set up the connection with the PSK broker process as configured
|
||||
/// via the `psk_broker_path`, `psk_broker_fd`, and `psk_broker_spawn`
|
||||
/// fields.
|
||||
#[cfg(feature = "experiment_api")]
|
||||
fn get_broker_socket(broker_interface: BrokerInterface) -> Result<UnixStream, anyhow::Error> {
|
||||
// Connect to the psk broker unix socket if one was specified
|
||||
@@ -549,7 +605,7 @@ impl CliArgs {
|
||||
}
|
||||
|
||||
/// generate secret and public keys, store in files according to the paths passed as arguments
|
||||
fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> {
|
||||
pub fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> {
|
||||
let mut ssk = crate::protocol::SSk::random();
|
||||
let mut spk = crate::protocol::SPk::random();
|
||||
StaticKem::keygen(ssk.secret_mut(), spk.deref_mut())?;
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
//! [`Rosenpass`] which holds such a configuration.
|
||||
//!
|
||||
//! ## TODO
|
||||
//! - support `~` in <https://github.com/rosenpass/rosenpass/issues/237>
|
||||
//! - provide tooling to create config file from shell <https://github.com/rosenpass/rosenpass/issues/247>
|
||||
//! - TODO: support `~` in <https://github.com/rosenpass/rosenpass/issues/237>
|
||||
//! - TODO: provide tooling to create config file from shell <https://github.com/rosenpass/rosenpass/issues/247>
|
||||
|
||||
use crate::protocol::{SPk, SSk};
|
||||
use rosenpass_util::file::LoadValue;
|
||||
use std::{
|
||||
@@ -31,7 +32,10 @@ fn empty_api_config() -> crate::api::config::ApiConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
/// Configuration for the Rosenpass key exchange
|
||||
///
|
||||
/// i.e. configuration for the `rosenpass exchange` and `rosenpass exchange-config` commands
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Rosenpass {
|
||||
// TODO: Raise error if secret key or public key alone is set during deserialization
|
||||
// SEE: https://github.com/serde-rs/serde/issues/2793
|
||||
@@ -46,7 +50,10 @@ pub struct Rosenpass {
|
||||
/// list of [`SocketAddr`] to listen on
|
||||
///
|
||||
/// Examples:
|
||||
/// - `0.0.0.0:123`
|
||||
///
|
||||
/// - `0.0.0.0:123` – Listen on any interface using IPv4, port 123
|
||||
/// - `[::1]:1234` – Listen on IPv6 localhost, port 1234
|
||||
/// - `[::]:4476` – Listen on any IPv4 or IPv6 interface, port 4476
|
||||
pub listen: Vec<SocketAddr>,
|
||||
|
||||
/// log verbosity
|
||||
@@ -68,6 +75,7 @@ pub struct Rosenpass {
|
||||
pub config_file_path: PathBuf,
|
||||
}
|
||||
|
||||
/// Public key and secret key locations.
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
|
||||
pub struct Keypair {
|
||||
/// path to the public key file
|
||||
@@ -78,6 +86,7 @@ pub struct Keypair {
|
||||
}
|
||||
|
||||
impl Keypair {
|
||||
/// Construct a keypair from its fields
|
||||
pub fn new<Pk: AsRef<Path>, Sk: AsRef<Path>>(public_key: Pk, secret_key: Sk) -> Self {
|
||||
let public_key = public_key.as_ref().to_path_buf();
|
||||
let secret_key = secret_key.as_ref().to_path_buf();
|
||||
@@ -88,62 +97,72 @@ impl Keypair {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## TODO
|
||||
/// - replace this type with [`log::LevelFilter`], also see <https://github.com/rosenpass/rosenpass/pull/246>
|
||||
/// Level of verbosity for [crate::app_server::AppServer]
|
||||
///
|
||||
/// The value of the field [crate::app_server::AppServer::verbosity]. See the field documentation
|
||||
/// for details.
|
||||
///
|
||||
/// - TODO: replace this type with [`log::LevelFilter`], also see <https://github.com/rosenpass/rosenpass/pull/246>
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone)]
|
||||
pub enum Verbosity {
|
||||
Quiet,
|
||||
Verbose,
|
||||
}
|
||||
|
||||
/// ## TODO
|
||||
/// - examples
|
||||
/// - documentation
|
||||
/// Configuration data for a single Rosenpass peer
|
||||
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RosenpassPeer {
|
||||
/// path to the public key of the peer
|
||||
pub public_key: PathBuf,
|
||||
|
||||
/// ## TODO
|
||||
/// - documentation
|
||||
/// The hostname and port to connect to
|
||||
///
|
||||
/// Can be a
|
||||
///
|
||||
/// - hostname and port, e.g. `localhost:8876` or `rosenpass.eu:1427`
|
||||
/// - IPv4 address and port, e.g. `1.2.3.4:7764`
|
||||
/// - IPv6 address and port, e.g. `[fe80::24]:7890`
|
||||
pub endpoint: Option<String>,
|
||||
|
||||
/// path to the pre-shared key with the peer
|
||||
/// path to the pre-shared key shared with the peer
|
||||
///
|
||||
/// NOTE: this item can be skipped in the config if you do not use a pre-shared key with the peer
|
||||
pub pre_shared_key: Option<PathBuf>,
|
||||
|
||||
/// ## TODO
|
||||
/// - documentation
|
||||
/// If this field is set to a path, the Rosenpass will write the exchanged symmetric keys
|
||||
/// to the given file and write a notification to standard out to let the calling application
|
||||
/// know that a new key was exchanged
|
||||
#[serde(default)]
|
||||
pub key_out: Option<PathBuf>,
|
||||
|
||||
/// ## TODO
|
||||
/// - documentation
|
||||
/// - make this field only available on binary builds, not on library builds <https://github.com/rosenpass/rosenpass/issues/249>
|
||||
/// Information for supplying exchanged keys directly to WireGuard
|
||||
#[serde(flatten)]
|
||||
pub wg: Option<WireGuard>,
|
||||
}
|
||||
|
||||
/// ## TODO
|
||||
/// - documentation
|
||||
/// Information for supplying exchanged keys directly to WireGuard
|
||||
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct WireGuard {
|
||||
/// ## TODO
|
||||
/// - documentation
|
||||
/// Name of the WireGuard interface to supply with pre-shared keys generated by the Rosenpass
|
||||
/// key exchange
|
||||
pub device: String,
|
||||
|
||||
/// ## TODO
|
||||
/// - documentation
|
||||
/// WireGuard public key of the peer to supply with pre-shared keys
|
||||
pub peer: String,
|
||||
|
||||
/// ## TODO
|
||||
/// - documentation
|
||||
/// Extra parameters passed to the `wg` command
|
||||
#[serde(default)]
|
||||
pub extra_params: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for Rosenpass {
|
||||
/// Generate an empty configuration
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
|
||||
#[doc = "```"]
|
||||
fn default() -> Self {
|
||||
Self::empty()
|
||||
}
|
||||
@@ -156,8 +175,15 @@ impl Rosenpass {
|
||||
/// checked whether they even exist.
|
||||
///
|
||||
/// ## TODO
|
||||
///
|
||||
/// - consider using a different algorithm to determine home directory – the below one may
|
||||
/// behave unexpectedly on Windows
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_store.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn load<P: AsRef<Path>>(p: P) -> anyhow::Result<Self> {
|
||||
// read file and deserialize
|
||||
let mut config: Self = toml::from_str(&fs::read_to_string(&p)?)?;
|
||||
@@ -185,7 +211,13 @@ impl Rosenpass {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Write a config to a file
|
||||
/// Encode a configuration object as toml and write it to a file
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_store.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn store<P: AsRef<Path>>(&self, p: P) -> anyhow::Result<()> {
|
||||
let serialized_config =
|
||||
toml::to_string_pretty(&self).expect("unable to serialize the default config");
|
||||
@@ -194,6 +226,12 @@ impl Rosenpass {
|
||||
}
|
||||
|
||||
/// Commit the configuration to where it came from, overwriting the original file
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_store.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn commit(&self) -> anyhow::Result<()> {
|
||||
let mut f = fopen_w(&self.config_file_path, Visibility::Public)?;
|
||||
f.write_all(toml::to_string_pretty(&self)?.as_bytes())?;
|
||||
@@ -201,13 +239,21 @@ impl Rosenpass {
|
||||
self.store(&self.config_file_path)
|
||||
}
|
||||
|
||||
/// Apply the configuration in this object to the given [crate::app_server::AppServer]
|
||||
pub fn apply_to_app_server(&self, _srv: &mut AppServer) -> anyhow::Result<()> {
|
||||
#[cfg(feature = "experiment_api")]
|
||||
self.api.apply_to_app_server(_srv)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate a configuration
|
||||
/// Check that the configuration is sound, ensuring
|
||||
/// for instance that the referenced files exist
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_validate.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn validate(&self) -> anyhow::Result<()> {
|
||||
if let Some(ref keypair) = self.keypair {
|
||||
// check the public key file exists
|
||||
@@ -284,6 +330,21 @@ impl Rosenpass {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check that the configuration is useful given the feature set Rosenpass was compiled with
|
||||
/// and the configuration values.
|
||||
///
|
||||
/// This was introduced when we introduced a unix-socket API feature allowing the server
|
||||
/// keypair to be supplied via the API; in this process we also made [Self::keypair] optional.
|
||||
/// With respect to this particular feature, this function ensures that [Self::keypair] is set
|
||||
/// when Rosenpass is compiles without the `experiment_api` flag. When `experiment_api` is
|
||||
/// used, the function ensures that [Self::keypair] is only `None`, if the Rosenpass API is
|
||||
/// enabled in the configuration.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_validate.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn check_usefullness(&self) -> anyhow::Result<()> {
|
||||
#[cfg(not(feature = "experiment_api"))]
|
||||
ensure!(self.keypair.is_some(), "Server keypair missing.");
|
||||
@@ -299,15 +360,38 @@ impl Rosenpass {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Produce an empty confuguration
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn empty() -> Self {
|
||||
Self::new(None)
|
||||
}
|
||||
|
||||
/// Produce configuration from the keypair
|
||||
///
|
||||
/// Shorthand for calling [Self::new] with Some([Keypair]::new(sk, pk)).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn from_sk_pk<Sk: AsRef<Path>, Pk: AsRef<Path>>(sk: Sk, pk: Pk) -> Self {
|
||||
Self::new(Some(Keypair::new(pk, sk)))
|
||||
}
|
||||
|
||||
/// Creates a new configuration
|
||||
/// Initialize a minimal configuration with the [Self::keypair] field supplied
|
||||
/// as a parameter
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn new(keypair: Option<Keypair>) -> Self {
|
||||
Self {
|
||||
keypair,
|
||||
@@ -321,6 +405,14 @@ impl Rosenpass {
|
||||
}
|
||||
|
||||
/// Add IPv4 __and__ IPv6 IF_ANY address to the listen interfaces
|
||||
///
|
||||
/// I.e. listen on any interface.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_add_if_any.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn add_if_any(&mut self, port: u16) {
|
||||
let ipv4_any = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port));
|
||||
let ipv6_any = SocketAddr::V6(SocketAddrV6::new(
|
||||
@@ -333,8 +425,20 @@ impl Rosenpass {
|
||||
self.listen.push(ipv6_any);
|
||||
}
|
||||
|
||||
/// from chaotic args
|
||||
/// Quest: the grammar is undecideable, what do we do here?
|
||||
/// Parser for the old, IP style grammar.
|
||||
///
|
||||
/// See out manual page rosenpass-exchange(1) on details about the grammar.
|
||||
///
|
||||
/// This function parses the grammar and turns it into an instance of the configuration
|
||||
/// struct.
|
||||
///
|
||||
/// TODO: the grammar is undecidable, what do we do here?
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
#[doc = "```ignore"]
|
||||
#[doc = include_str!("../tests/config_Rosenpass_parse_args_simple.rs")]
|
||||
#[doc = "```"]
|
||||
pub fn parse_args(args: Vec<String>) -> anyhow::Result<Self> {
|
||||
let mut config = Self::new(Some(Keypair::new("", "")));
|
||||
|
||||
@@ -525,11 +629,13 @@ impl Rosenpass {
|
||||
}
|
||||
|
||||
impl Default for Verbosity {
|
||||
/// Self::Quiet
|
||||
fn default() -> Self {
|
||||
Self::Quiet
|
||||
}
|
||||
}
|
||||
|
||||
/// Example configuration generated by the command `rosenpass gen-config <TOML-FILE>`.
|
||||
pub static EXAMPLE_CONFIG: &str = r###"public_key = "/path/to/rp-public-key"
|
||||
secret_key = "/path/to/rp-secret-key"
|
||||
listen = []
|
||||
@@ -553,7 +659,7 @@ key_out = "/path/to/rp-key-out.txt" # path to store the key
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
use std::{borrow::Borrow, net::IpAddr};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
fn toml_des<S: Borrow<str>>(s: S) -> Result<toml::Table, toml::de::Error> {
|
||||
toml::from_str(s.borrow())
|
||||
@@ -664,37 +770,6 @@ mod test {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_cli_parse() {
|
||||
let args = split_str(
|
||||
"public-key /my/public-key secret-key /my/secret-key verbose \
|
||||
listen 0.0.0.0:9999 peer public-key /peer/public-key endpoint \
|
||||
peer.test:9999 outfile /peer/rp-out",
|
||||
);
|
||||
|
||||
let config = Rosenpass::parse_args(args).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.keypair,
|
||||
Some(Keypair::new("/my/public-key", "/my/secret-key"))
|
||||
);
|
||||
assert_eq!(config.verbosity, Verbosity::Verbose);
|
||||
assert_eq!(
|
||||
&config.listen,
|
||||
&vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9999)]
|
||||
);
|
||||
assert_eq!(
|
||||
config.peers,
|
||||
vec![RosenpassPeer {
|
||||
public_key: PathBuf::from("/peer/public-key"),
|
||||
endpoint: Some("peer.test:9999".into()),
|
||||
pre_shared_key: None,
|
||||
key_out: Some(PathBuf::from("/peer/rp-out")),
|
||||
..Default::default()
|
||||
}]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cli_parse_multiple_peers() {
|
||||
let args = split_str(
|
||||
|
||||
10
rosenpass/tests/config_Rosenpass_add_if_any.rs
Normal file
10
rosenpass/tests/config_Rosenpass_add_if_any.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use rosenpass::config::Rosenpass;
|
||||
|
||||
#[test]
|
||||
fn config_Rosenpass_add_if_any_example() {
|
||||
let mut v = Rosenpass::empty();
|
||||
v.add_if_any(4000);
|
||||
|
||||
assert!(v.listen.iter().any(|a| format!("{a:?}") == "0.0.0.0:4000"));
|
||||
assert!(v.listen.iter().any(|a| format!("{a:?}") == "[::]:4000"));
|
||||
}
|
||||
18
rosenpass/tests/config_Rosenpass_new.rs
Normal file
18
rosenpass/tests/config_Rosenpass_new.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use rosenpass::config::{Keypair, Rosenpass};
|
||||
|
||||
#[test]
|
||||
fn example_config_rosenpass_new() {
|
||||
let (sk, pk) = ("./example.sk", "./example.pk");
|
||||
|
||||
assert_eq!(Rosenpass::empty(), Rosenpass::new(None));
|
||||
assert_eq!(Rosenpass::empty(), Rosenpass::default());
|
||||
|
||||
assert_eq!(
|
||||
Rosenpass::from_sk_pk(sk, pk),
|
||||
Rosenpass::new(Some(Keypair::new(pk, sk)))
|
||||
);
|
||||
|
||||
let mut v = Rosenpass::empty();
|
||||
v.keypair = Some(Keypair::new(pk, sk));
|
||||
assert_eq!(Rosenpass::from_sk_pk(sk, pk), v);
|
||||
}
|
||||
36
rosenpass/tests/config_Rosenpass_parse_args_simple.rs
Normal file
36
rosenpass/tests/config_Rosenpass_parse_args_simple.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use rosenpass::config::{Keypair, Rosenpass, RosenpassPeer, Verbosity};
|
||||
|
||||
#[test]
|
||||
fn parse_simple() {
|
||||
let argv = "public-key /my/public-key secret-key /my/secret-key verbose \
|
||||
listen 0.0.0.0:9999 peer public-key /peer/public-key endpoint \
|
||||
peer.test:9999 outfile /peer/rp-out";
|
||||
let argv = argv.split(' ').map(|s| s.to_string()).collect();
|
||||
|
||||
let config = Rosenpass::parse_args(argv).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.keypair,
|
||||
Some(Keypair::new("/my/public-key", "/my/secret-key"))
|
||||
);
|
||||
assert_eq!(config.verbosity, Verbosity::Verbose);
|
||||
assert_eq!(
|
||||
&config.listen,
|
||||
&vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9999)]
|
||||
);
|
||||
assert_eq!(
|
||||
config.peers,
|
||||
vec![RosenpassPeer {
|
||||
public_key: PathBuf::from("/peer/public-key"),
|
||||
endpoint: Some("peer.test:9999".into()),
|
||||
pre_shared_key: None,
|
||||
key_out: Some(PathBuf::from("/peer/rp-out")),
|
||||
..Default::default()
|
||||
}]
|
||||
);
|
||||
}
|
||||
42
rosenpass/tests/config_Rosenpass_store.rs
Normal file
42
rosenpass/tests/config_Rosenpass_store.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rosenpass::config::{Rosenpass, Verbosity};
|
||||
|
||||
#[test]
|
||||
fn example_config_rosenpass_store() -> anyhow::Result<()> {
|
||||
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();
|
||||
|
||||
let tmpdir = tempfile::tempdir()?;
|
||||
|
||||
let sk = tmpdir.path().join("example.sk");
|
||||
let pk = tmpdir.path().join("example.pk");
|
||||
let cfg = tmpdir.path().join("config.toml");
|
||||
|
||||
let mut c = Rosenpass::from_sk_pk(&sk, &pk);
|
||||
|
||||
// Can not commit config, path not known
|
||||
assert!(c.commit().is_err());
|
||||
|
||||
// We can store it to an explicit path though
|
||||
c.store(&cfg)?;
|
||||
|
||||
// Storing does not set commitment path
|
||||
assert!(c.commit().is_err());
|
||||
|
||||
// We can reload the config now and the configurations
|
||||
// are equal if we adjust the commitment path
|
||||
let mut c2 = Rosenpass::load(&cfg)?;
|
||||
c.config_file_path = PathBuf::from(&cfg);
|
||||
assert_eq!(c, c2);
|
||||
|
||||
// And this loaded config can now be committed
|
||||
c2.verbosity = Verbosity::Verbose;
|
||||
c2.commit()?;
|
||||
|
||||
// And the changes actually made it to disk
|
||||
let c3 = Rosenpass::load(cfg)?;
|
||||
assert_eq!(c2, c3);
|
||||
assert_ne!(c, c3);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
37
rosenpass/tests/config_Rosenpass_validate.rs
Normal file
37
rosenpass/tests/config_Rosenpass_validate.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use std::fs;
|
||||
|
||||
use rosenpass::{cli::generate_and_save_keypair, config::Rosenpass};
|
||||
|
||||
#[test]
|
||||
fn example_config_rosenpass_validate() -> anyhow::Result<()> {
|
||||
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();
|
||||
|
||||
let tmpdir = tempfile::tempdir()?;
|
||||
|
||||
// Empty validates OK
|
||||
assert!(Rosenpass::empty().validate().is_ok());
|
||||
|
||||
// Missing secret key does not pass usefulness
|
||||
assert!(Rosenpass::empty().check_usefullness().is_err());
|
||||
|
||||
let sk = tmpdir.path().join("example.sk");
|
||||
let pk = tmpdir.path().join("example.pk");
|
||||
let cfg = Rosenpass::from_sk_pk(&sk, &pk);
|
||||
|
||||
// Missing secret key does not validate
|
||||
assert!(cfg.validate().is_err());
|
||||
|
||||
// But passes usefulness (the configuration is useful but invalid)
|
||||
assert!(cfg.check_usefullness().is_ok());
|
||||
|
||||
// Providing empty key files does not help
|
||||
fs::write(&sk, b"")?;
|
||||
fs::write(&pk, b"")?;
|
||||
assert!(cfg.validate().is_err());
|
||||
|
||||
// But after providing proper key files, the configuration validates
|
||||
generate_and_save_keypair(sk, pk)?;
|
||||
assert!(cfg.validate().is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user