diff --git a/src/cmd/renc.rs b/src/cmd/renc.rs index fba8e03..1026d92 100644 --- a/src/cmd/renc.rs +++ b/src/cmd/renc.rs @@ -1,6 +1,8 @@ +use blake3::Hasher; use eyre::{eyre, ContextCompat, Result}; use spdlog::{debug, error, info, trace}; use std::{ + collections::HashMap, fs::{self, File}, io::{Read, Write}, iter, @@ -9,14 +11,13 @@ use std::{ }; use crate::{ - cmd::stored_sec_path::{SecretBufferMap, SecretPathMap}, - profile::{MasterIdentity, Profile, Settings}, + cmd::stored_sec_path::{HashWithCtx, InCfg, SecMap, SecPath}, + profile::{MasterIdentity, Profile}, }; use crate::{interop::add_to_store, profile}; use age::{x25519, Identity}; -use super::stored_sec_path::StoredSecretPath; use crate::helper::parse_identity::ParsedIdentity; impl Profile { pub fn get_key_pair_iter<'a>(&'a self) -> impl Iterator> + 'a { @@ -56,6 +57,7 @@ impl Profile { let renc_path = { let mut p = flake_root.clone(); p.push(self.settings.storage_dir_relative.clone()); + let p = p.canonicalize()?; info!( "reading user identity encrypted dir under flake root: {}", p.display() @@ -63,8 +65,10 @@ impl Profile { p }; - // from secrets metadata, read real config store - let sec_buf: SecretBufferMap = SecretPathMap::from_secret_set(&self.secrets).into(); + // from secrets metadata, from real config store + let data = SecMap::>::from(self.secrets.clone(), renc_path.clone()); + + let data_renc_path_map = data.clone().calc_renc(self.settings.host_pubkey.clone())?; let decrypt = |buffer: &Vec, key: &dyn Identity| -> Result> { let decryptor = age::Decryptor::new(&buffer[..])?; @@ -79,110 +83,47 @@ impl Profile { Ok(decrypted) }; - // WARN: this failed while using plugin - let avail_ident = if let Some(found) = key_pair_list.find(|k| k.is_ok()) { - if let Ok(r) = found { - r - } else { - return Err(eyre!("no available key for this material")); - } - } else { - return Err(eyre!("provided identities not valid")); - }; - - let key = avail_ident.get_identity(); - let sec_buf = sec_buf.inner(); - let decrypted_iter = sec_buf.iter().filter_map(|(s, b)| { - let decrypted = decrypt(b, &**key); - Some((s, decrypted)) - }); - - let recip_host_pubkey = ssh::Recipient::from_str(self.settings.host_pubkey.as_str()); - - let recip_unwrap = recip_host_pubkey.unwrap(); - - let encrypted_iter = decrypted_iter.filter_map(|(s, b)| { - let m = SecretPathMap::from_profile(&self) - .to_flake_repo_relative_renced_path(&self, flake_root.clone()); - let buffer = if let Err(e) = b { - error!("{}", e); - return None; - } else { - b.expect("there") - }; - - let b_hash = blake3::hash(&buffer); - - if let Some(o) = m.inner().get(s) { - let flake_renc = fs::read(o.clone().inner()); - if let Ok(c) = flake_renc { - let ctx = { - let decryptor = age::Decryptor::new(&c[..]).expect(""); - - let mut decrypted = vec![]; - let mut reader = decryptor - .decrypt(iter::once( - &self - .settings - .host_keys // TODO: cannot compare on remote machine - .get(0) - .unwrap() - .get_identity() - .unwrap() as &dyn age::Identity, - )) - .expect("decrypt fail"); - let res = reader.read_to_end(&mut decrypted); - if let Ok(b) = res { - debug!("decrypted secret in store: {} bytes", b); - } - - decrypted - }; - trace!("checking hash{:?}", c); - - let c_hash = blake3::hash(&ctx); - trace!("hash: prev {} after {}", c_hash, b_hash); - if c_hash == b_hash { - // skip - info!("skip unchanged file: {}", s.name); - return None; - } - } - } + let parsed_ident = key_pair_list + .find(|k| k.is_ok()) + .wrap_err_with(|| eyre!("available keypair not found"))??; - let encryptor = age::Encryptor::with_recipients(iter::once(&recip_unwrap as _)) - .expect("a recipient"); - let mut out_buf = vec![]; + let key = parsed_ident.get_identity(); - let mut writer = encryptor.wrap_output(&mut out_buf).unwrap(); + let sec_need_renc = data_renc_path_map + .inner() + .into_iter() + .filter(|(k, v)| { + let hash = v.get_hash(); + let renc_path = { + let mut path = renc_path.clone(); + path.push(hash.to_string()); + path.canonicalize().expect("no err") + }; - writer.write_all(&buffer[..]).unwrap(); - writer.finish().unwrap(); + debug!("comparing {}", renc_path.display()); - Some((s, out_buf)) - }); + let exs = renc_path.exists(); - trace!("re encrypted: {:?}", encrypted_iter); - - info!("cleaning old re-encryption extract dir"); - let ren = SecretPathMap::from_profile(&self).inner(); - encrypted_iter.for_each(|(s, b)| { - let mut to_create = renc_path.clone(); - - let renced_store_path = ren.get(s).cloned().unwrap().inner(); - to_create.push(renced_store_path.file_name().unwrap()); - - debug!("path string {:?}", to_create); - let mut fd = File::create(to_create).expect("create file error"); - let _ = fd.write_all(&b[..]); - }); + if exs { + info!("skipping {} since exist", k.id) + } - let o = add_to_store(renc_path)?; - if !o.status.success() { - error!("Command executed with failing error code"); - } - // Another side, calculate with nix `builtins.path` and pass to when deploy as `storage` - info!("path added to store: {}", String::from_utf8(o.stdout)?); + !exs + }) + .collect::>() + .into_keys() + .collect::>(); + + // data.bake().and_then(|b| { + // b.inner().into_iter().filter(|(s,_)| sec_need_renc.contains(s)).for_each(|(k,v)|) + // }) + + // let o = add_to_store(renc_path)?; + // if !o.status.success() { + // error!("Command executed with failing error code"); + // } + // // Another side, calculate with nix `builtins.path` and pass to when deploy as `storage` + // info!("path added to store: {}", String::from_utf8(o.stdout)?); Ok(()) } } diff --git a/src/cmd/stored_sec_path.rs b/src/cmd/stored_sec_path.rs index a671ed6..75471ae 100644 --- a/src/cmd/stored_sec_path.rs +++ b/src/cmd/stored_sec_path.rs @@ -5,6 +5,7 @@ use std::{ path::{Path, PathBuf}, }; +use age::Identity; use eyre::{Context, ContextCompat}; use crate::profile::{self, Profile, SecretSet, Settings}; @@ -12,12 +13,14 @@ use eyre::{eyre, Result}; use std::marker::PhantomData; #[derive(Debug, Clone)] -pub struct SecretPath, T> { +pub struct SecPath, T> { path: P, _marker: PhantomData, } +#[derive(Debug, Clone)] pub struct InStore; +#[derive(Debug, Clone)] pub struct InCfg; pub trait GetSec { @@ -25,19 +28,19 @@ pub trait GetSec { fn open_file(&self) -> Result; } -impl SecretPath +impl SecPath where P: AsRef, { pub fn new(path: P) -> Self { - SecretPath { + SecPath { path, _marker: PhantomData, } } } -impl GetSec for SecretPath +impl GetSec for SecPath where P: AsRef, { @@ -54,45 +57,58 @@ where } } -impl FromIterator<(profile::Secret, Vec)> for SecretPathMap> { - fn from_iter)>>(iter: I) -> Self { - let map = HashMap::from_iter(iter); - SecretPathMap(map) - } +macro_rules! impl_from_iterator_for_secmap { + ($($t:ty),*) => { + $( + impl FromIterator<(profile::Secret, $t)> for SecMap<$t> { + fn from_iter>(iter: I) -> Self { + let map = HashMap::from_iter(iter); + SecMap(map) + } + } + )* + }; } -type HashWithCtx = (blake3::Hash, Vec); +impl_from_iterator_for_secmap!(Vec, HashWithCtx, PathWithCtx); -impl FromIterator<(profile::Secret, HashWithCtx)> for SecretPathMap { - fn from_iter>(iter: I) -> Self { - let map = HashMap::from_iter(iter); - SecretPathMap(map) +pub struct HashWithCtx(blake3::Hash, Vec); + +impl HashWithCtx { + pub fn new(b: blake3::Hash, v: Vec) -> Self { + HashWithCtx(b, v) + } + pub fn get_hash(&self) -> &blake3::Hash { + &self.0 + } + pub fn get_ctx(&self) -> &Vec { + &self.1 } } #[derive(Debug, Clone)] -pub struct SecretPathMap

(HashMap); +pub struct SecMap

(HashMap); -impl SecretPathMap { +impl SecMap { pub fn inner(self) -> HashMap { self.0 } } -impl SecretPathMap> { +impl SecMap> { /// read secret file - pub fn bake(self) -> Result>> { + pub fn bake_ctx(self) -> Result>> { self.inner() .into_iter() .map(|(k, v)| v.read_buffer().and_then(|b| Ok((k, b)))) - .try_collect::>>() + .try_collect::>>() } /// hash of encrypted file content /// used in: renc, calc and compare /// deploy, calc and find in store - pub fn calc_renc(self, _host_pubkey: String) -> Result> { - self.bake().and_then(|h| { + pub fn calc_renc(self, _host_pubkey: String) -> Result> { + self.bake_ctx().and_then(|h| { h.inner() .into_iter() .map(|(k, v)| { @@ -100,37 +116,78 @@ impl SecretPathMap> { hasher.update(v.as_slice()); // hasher.update(host_pubkey.as_bytes()); let hash = hasher.finalize(); - Ok((k, (hash, v))) + Ok((k, HashWithCtx::new(hash, v))) }) - .try_collect::>() + .try_collect::>() }) } } -impl SecretPathMap> { +impl SecMap> { pub fn from(secrets: SecretSet) -> Self { let res = secrets .into_values() .into_iter() .map(|s| { - let secret_path = SecretPath::<_, InStore>::new(PathBuf::from(s.file.clone())); + let secret_path = SecPath::<_, InStore>::new(PathBuf::from(s.file.clone())); (s, secret_path) }) .collect(); - SecretPathMap::>(res) + SecMap::>(res) } } -impl SecretPathMap> { - pub fn from(secrets: SecretSet) -> Self { +impl SecMap> { + pub fn from(secrets: SecretSet, storage_abs_cfg: PathBuf) -> Self { let res = secrets .into_values() .into_iter() .map(|s| { - let secret_path = SecretPath::<_, InCfg>::new(PathBuf::from(s.file.clone())); - (s, secret_path) + s.file + .clone() + .split_once('-') + .and_then(|(_, n)| Some(n)) + .wrap_err_with(|| eyre!("something wrong with secret file name in store")) + .and_then(|file_n| { + let mut path = storage_abs_cfg.clone(); + path.push(file_n); + let secret_path = SecPath::<_, InCfg>::new(path); + Ok((s, secret_path)) + }) }) - .collect(); - SecretPathMap::>(res) + .try_collect() + .expect("ok"); + SecMap::>(res) + } + + pub fn makeup(self, enc: F) -> Result<()> + where + F: Fn(&Vec, &dyn Identity) -> Result>, + { + Ok(()) + } + + pub fn write(self) -> Result<()> { + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub struct PathWithCtx(SecPath, Vec); + +impl From>> for SecMap { + fn from(value: SecMap>) -> Self { + value + .inner() + .into_iter() + .filter_map(|(s, p)| { + let mut f = p.open_file().ok()?; + let mut buffer = Vec::new(); + f.read_to_end(&mut buffer) + .wrap_err_with(|| eyre!("read secret file error")) + .ok()?; + Some((s, PathWithCtx(p, buffer))) + }) + .collect() } }