|
@@ -4,7 +4,7 @@ use btconfig::{
|
|
|
use btlib::{
|
|
|
bterr,
|
|
|
crypto::{CredStore, CredStoreMut, Creds},
|
|
|
- Decompose, Epoch, Principal, Principaled, RelBlockPath, Result, Writecap,
|
|
|
+ BlockPath, Epoch, Principal, Principaled, Result, Writecap,
|
|
|
};
|
|
|
use btserde::{read_from, write_to};
|
|
|
use figment::{providers::Serialized, Figment};
|
|
@@ -19,39 +19,44 @@ use tempdir::TempDir;
|
|
|
use termion::input::TermRead;
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
-struct AppConfig {
|
|
|
+pub struct BtprovisionConfig {
|
|
|
/// The configuration for the [CredStore] to use.
|
|
|
- credstore: CredStoreConfig,
|
|
|
+ #[serde(rename = "credstore")]
|
|
|
+ pub cred_store: CredStoreConfig,
|
|
|
/// The root password.
|
|
|
- password: Option<String>,
|
|
|
+ pub password: Option<String>,
|
|
|
/// The number of seconds after the UNIX epoch when the credentials expire.
|
|
|
- writecapexpires: Option<u64>,
|
|
|
- writecappath: Option<String>,
|
|
|
- writecapissuee: Option<String>,
|
|
|
- writecapsavepath: Option<PathBuf>,
|
|
|
+ #[serde(rename = "writecapexpires")]
|
|
|
+ pub writecap_expires: Option<u64>,
|
|
|
+ #[serde(rename = "writecappath")]
|
|
|
+ pub writecap_path: Option<String>,
|
|
|
+ #[serde(rename = "writecapissuee")]
|
|
|
+ pub writecap_issuee: Option<String>,
|
|
|
+ #[serde(rename = "writecapsavepath")]
|
|
|
+ pub writecap_save_path: Option<PathBuf>,
|
|
|
}
|
|
|
|
|
|
-impl AppConfig {
|
|
|
- fn new() -> Result<Self> {
|
|
|
+impl BtprovisionConfig {
|
|
|
+ pub fn new() -> Result<Self> {
|
|
|
Figment::new()
|
|
|
- .merge(Serialized::defaults(AppConfig::default()))
|
|
|
+ .merge(Serialized::defaults(BtprovisionConfig::default()))
|
|
|
.btconfig()?
|
|
|
.extract()
|
|
|
.map_err(|err| err.into())
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Default for AppConfig {
|
|
|
+impl Default for BtprovisionConfig {
|
|
|
fn default() -> Self {
|
|
|
const DEFAULT_VALID_FOR: u64 = 60 * 60 * 24 * 365;
|
|
|
let expires = Epoch::now() + Duration::from_secs(DEFAULT_VALID_FOR);
|
|
|
Self {
|
|
|
- credstore: CredStoreConfig::default(),
|
|
|
+ cred_store: CredStoreConfig::default(),
|
|
|
password: None,
|
|
|
- writecapexpires: Some(expires.value()),
|
|
|
- writecappath: None,
|
|
|
- writecapissuee: None,
|
|
|
- writecapsavepath: None,
|
|
|
+ writecap_expires: Some(expires.value()),
|
|
|
+ writecap_path: None,
|
|
|
+ writecap_issuee: None,
|
|
|
+ writecap_save_path: None,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -85,7 +90,7 @@ impl<'a> CredStoreMutConsumer for RootProvisionConsumer<'a> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn gen_root_creds(config: AppConfig) -> Result<()> {
|
|
|
+fn gen_root_creds(config: BtprovisionConfig) -> Result<()> {
|
|
|
let password = if let Some(password) = config.password {
|
|
|
password
|
|
|
} else {
|
|
@@ -96,15 +101,15 @@ fn gen_root_creds(config: AppConfig) -> Result<()> {
|
|
|
}
|
|
|
password
|
|
|
};
|
|
|
- let expires = get_setting!(config, writecapexpires);
|
|
|
- config.credstore.consume_mut(RootProvisionConsumer {
|
|
|
+ let expires = get_setting!(config, writecap_expires);
|
|
|
+ config.cred_store.consume_mut(RootProvisionConsumer {
|
|
|
password: &password,
|
|
|
expires: Epoch::from_value(expires),
|
|
|
})?
|
|
|
}
|
|
|
|
|
|
-fn gen_node_creds(config: AppConfig) -> Result<()> {
|
|
|
- let principal = config.credstore.consume(PrincipalConsumer)??;
|
|
|
+fn gen_node_creds(config: BtprovisionConfig) -> Result<()> {
|
|
|
+ let principal = config.cred_store.consume(PrincipalConsumer)??;
|
|
|
eprint!("node principal: ");
|
|
|
println!("{principal}");
|
|
|
Ok(())
|
|
@@ -113,7 +118,7 @@ fn gen_node_creds(config: AppConfig) -> Result<()> {
|
|
|
struct IssueWritecapConsumer<'a> {
|
|
|
password: &'a str,
|
|
|
issuee: Principal,
|
|
|
- components: Vec<String>,
|
|
|
+ components: String,
|
|
|
expires: Epoch,
|
|
|
}
|
|
|
|
|
@@ -121,27 +126,32 @@ impl<'a> CredStoreConsumer for IssueWritecapConsumer<'a> {
|
|
|
type Output = Result<Writecap>;
|
|
|
fn consume<C: CredStore>(self, cred_store: C) -> Self::Output {
|
|
|
let root_creds = cred_store.root_creds(self.password)?;
|
|
|
- root_creds.issue_writecap(self.issuee, self.components, self.expires)
|
|
|
+ let mut components = self.components.split(BlockPath::SEP);
|
|
|
+ root_creds.issue_writecap(self.issuee, &mut components, self.expires)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn issue_node_writecap(config: AppConfig) -> Result<()> {
|
|
|
- let (writecap_path, issuee, expires) =
|
|
|
- get_settings!(config, writecapsavepath, writecapissuee, writecapexpires);
|
|
|
+fn issue_node_writecap(config: BtprovisionConfig) -> Result<()> {
|
|
|
+ let (writecap_path, issuee, expires) = get_settings!(
|
|
|
+ config,
|
|
|
+ writecap_save_path,
|
|
|
+ writecap_issuee,
|
|
|
+ writecap_expires
|
|
|
+ );
|
|
|
let password = if let Some(password) = config.password {
|
|
|
password
|
|
|
} else {
|
|
|
password_prompt("Please enter the root password: ")?
|
|
|
};
|
|
|
- let cred_components = if let Some(cred_path) = config.writecappath {
|
|
|
- RelBlockPath::try_from(cred_path.as_str())?
|
|
|
+ let cred_components = if let Some(cred_path) = config.writecap_path {
|
|
|
+ cred_path
|
|
|
} else {
|
|
|
- RelBlockPath::empty()
|
|
|
+ String::new()
|
|
|
};
|
|
|
let issuee = Principal::try_from(issuee.as_str())?;
|
|
|
- let writecap = config.credstore.consume(IssueWritecapConsumer {
|
|
|
+ let writecap = config.cred_store.consume(IssueWritecapConsumer {
|
|
|
password: &password,
|
|
|
- components: cred_components.into_inner(),
|
|
|
+ components: cred_components,
|
|
|
expires: Epoch::from_value(expires),
|
|
|
issuee,
|
|
|
})??;
|
|
@@ -173,9 +183,9 @@ impl CredStoreMutConsumer for SaveNodeWritecapConsumer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-fn save_node_writecap(config: AppConfig) -> Result<()> {
|
|
|
+fn save_node_writecap(config: BtprovisionConfig) -> Result<()> {
|
|
|
let writecap = {
|
|
|
- let writecap_path = get_setting!(config, writecapsavepath);
|
|
|
+ let writecap_path = get_setting!(config, writecap_save_path);
|
|
|
let file = OpenOptions::new()
|
|
|
.read(true)
|
|
|
.write(false)
|
|
@@ -185,7 +195,7 @@ fn save_node_writecap(config: AppConfig) -> Result<()> {
|
|
|
read_from::<Writecap, _>(&mut reader)?
|
|
|
};
|
|
|
config
|
|
|
- .credstore
|
|
|
+ .cred_store
|
|
|
.consume_mut(SaveNodeWritecapConsumer { writecap })?
|
|
|
}
|
|
|
|
|
@@ -199,14 +209,14 @@ impl CredStoreConsumer for PrincipalConsumer {
|
|
|
}
|
|
|
|
|
|
/// Runs all of the provisioning steps on a single credential store.
|
|
|
-fn full(mut config: AppConfig) -> Result<()> {
|
|
|
+fn full(mut config: BtprovisionConfig) -> Result<()> {
|
|
|
gen_root_creds(config.clone())?;
|
|
|
gen_node_creds(config.clone())?;
|
|
|
- let node_principal = config.credstore.clone().consume(PrincipalConsumer)??;
|
|
|
- config.writecapissuee = Some(node_principal.to_string());
|
|
|
- let _temp_dir = if config.writecapsavepath.is_none() {
|
|
|
+ let node_principal = config.cred_store.clone().consume(PrincipalConsumer)??;
|
|
|
+ config.writecap_issuee = Some(node_principal.to_string());
|
|
|
+ let _temp_dir = if config.writecap_save_path.is_none() {
|
|
|
let temp_dir = TempDir::new("btprovision")?;
|
|
|
- config.writecapsavepath = Some(temp_dir.path().join("writecap"));
|
|
|
+ config.writecap_save_path = Some(temp_dir.path().join("writecap"));
|
|
|
Some(temp_dir)
|
|
|
} else {
|
|
|
None
|
|
@@ -215,7 +225,7 @@ fn full(mut config: AppConfig) -> Result<()> {
|
|
|
save_node_writecap(config)
|
|
|
}
|
|
|
|
|
|
-fn run(command: &str, config: AppConfig) -> Result<()> {
|
|
|
+fn run(command: &str, config: BtprovisionConfig) -> Result<()> {
|
|
|
match command {
|
|
|
"gen_root_creds" => gen_root_creds(config),
|
|
|
"gen_node_creds" => gen_node_creds(config),
|
|
@@ -227,7 +237,7 @@ fn run(command: &str, config: AppConfig) -> Result<()> {
|
|
|
}
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
- let config = AppConfig::new()?;
|
|
|
+ let config = BtprovisionConfig::new()?;
|
|
|
let mut args = std::env::args().skip(1);
|
|
|
let command = args
|
|
|
.next()
|
|
@@ -237,9 +247,12 @@ fn main() -> Result<()> {
|
|
|
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
+ use std::time::Duration;
|
|
|
+
|
|
|
use super::*;
|
|
|
|
|
|
- use btlib::{crypto::CredsPriv, BlockError};
|
|
|
+ use btconfig::CredStoreConfig;
|
|
|
+ use btlib::{crypto::CredsPriv, BlockError, RelBlockPath};
|
|
|
use tempdir::TempDir;
|
|
|
|
|
|
struct NodeWritecapConsumer;
|
|
@@ -289,13 +302,13 @@ mod tests {
|
|
|
|
|
|
run(
|
|
|
"gen_root_creds",
|
|
|
- AppConfig {
|
|
|
- credstore: root_store.clone(),
|
|
|
+ BtprovisionConfig {
|
|
|
+ cred_store: root_store.clone(),
|
|
|
password: Some(password.clone()),
|
|
|
- writecapexpires: Some(expires_expected),
|
|
|
- writecappath: None,
|
|
|
- writecapissuee: None,
|
|
|
- writecapsavepath: None,
|
|
|
+ writecap_expires: Some(expires_expected),
|
|
|
+ writecap_path: None,
|
|
|
+ writecap_issuee: None,
|
|
|
+ writecap_save_path: None,
|
|
|
},
|
|
|
)
|
|
|
.unwrap();
|
|
@@ -313,13 +326,13 @@ mod tests {
|
|
|
}
|
|
|
run(
|
|
|
"gen_node_creds",
|
|
|
- AppConfig {
|
|
|
- credstore: node_store.clone(),
|
|
|
+ BtprovisionConfig {
|
|
|
+ cred_store: node_store.clone(),
|
|
|
password: None,
|
|
|
- writecapexpires: None,
|
|
|
- writecappath: None,
|
|
|
- writecapissuee: None,
|
|
|
- writecapsavepath: None,
|
|
|
+ writecap_expires: None,
|
|
|
+ writecap_path: None,
|
|
|
+ writecap_issuee: None,
|
|
|
+ writecap_save_path: None,
|
|
|
},
|
|
|
)
|
|
|
.unwrap();
|
|
@@ -331,25 +344,25 @@ mod tests {
|
|
|
.to_string();
|
|
|
run(
|
|
|
"issue_node_writecap",
|
|
|
- AppConfig {
|
|
|
- credstore: root_store,
|
|
|
+ BtprovisionConfig {
|
|
|
+ cred_store: root_store,
|
|
|
password: Some(password),
|
|
|
- writecapexpires: Some(expires_expected),
|
|
|
- writecappath: Some(writecap_path.clone()),
|
|
|
- writecapissuee: Some(issued_to_expected.clone()),
|
|
|
- writecapsavepath: Some(writecap_save_path.clone()),
|
|
|
+ writecap_expires: Some(expires_expected),
|
|
|
+ writecap_path: Some(writecap_path.clone()),
|
|
|
+ writecap_issuee: Some(issued_to_expected.clone()),
|
|
|
+ writecap_save_path: Some(writecap_save_path.clone()),
|
|
|
},
|
|
|
)
|
|
|
.unwrap();
|
|
|
run(
|
|
|
"save_node_writecap",
|
|
|
- AppConfig {
|
|
|
- credstore: node_store.clone(),
|
|
|
+ BtprovisionConfig {
|
|
|
+ cred_store: node_store.clone(),
|
|
|
password: None,
|
|
|
- writecapexpires: None,
|
|
|
- writecappath: None,
|
|
|
- writecapissuee: None,
|
|
|
- writecapsavepath: Some(writecap_save_path),
|
|
|
+ writecap_expires: None,
|
|
|
+ writecap_path: None,
|
|
|
+ writecap_issuee: None,
|
|
|
+ writecap_save_path: Some(writecap_save_path),
|
|
|
},
|
|
|
)
|
|
|
.unwrap();
|
|
@@ -369,3 +382,56 @@ mod tests {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod config_tests {
|
|
|
+ use super::BtprovisionConfig;
|
|
|
+
|
|
|
+ use std::path::PathBuf;
|
|
|
+
|
|
|
+ use figment::Jail;
|
|
|
+
|
|
|
+ macro_rules! test_field {
|
|
|
+ ($field:ident, $expected:expr, $env_var:literal) => {
|
|
|
+ #[test]
|
|
|
+ fn $field() {
|
|
|
+ Jail::expect_with(|jail| {
|
|
|
+ jail.set_env($env_var, $expected);
|
|
|
+
|
|
|
+ let config = BtprovisionConfig::new().unwrap();
|
|
|
+
|
|
|
+ assert_eq!(Some($expected), config.$field);
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+ })
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ test_field!(password, String::from("eldritch"), "BT_PASSWORD");
|
|
|
+ test_field!(writecap_expires, 1729, "BT_WRITECAPEXPIRES");
|
|
|
+ test_field!(
|
|
|
+ writecap_path,
|
|
|
+ String::from("foofercoorg"),
|
|
|
+ "BT_WRITECAPPATH"
|
|
|
+ );
|
|
|
+ test_field!(
|
|
|
+ writecap_issuee,
|
|
|
+ String::from("slammin"),
|
|
|
+ "BT_WRITECAPISSUEE"
|
|
|
+ );
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn writecap_save_path() {
|
|
|
+ Jail::expect_with(|jail| {
|
|
|
+ let expected = PathBuf::from("./writecap");
|
|
|
+ jail.set_env("BT_WRITECAPSAVEPATH", expected.display());
|
|
|
+
|
|
|
+ let config = BtprovisionConfig::new().unwrap();
|
|
|
+
|
|
|
+ assert_eq!(Some(expected), config.writecap_save_path);
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|