1use std::str::FromStr;23use crate::{command::CommandExt, host::Config};4use anyhow::{anyhow, Result};5use log::warn;67impl Config {8 pub fn cached_key(&self, host: &str) -> Option<String> {9 let data = self.data();10 let key = data.hosts.get(host).map(|h| &h.encryption_key);11 if let Some(key) = key {12 if key.is_empty() {13 return None;14 }15 }16 key.cloned()17 }18 pub fn update_key(&self, host: &str, key: String) {19 let mut data = self.data_mut();20 let host = data.hosts.entry(host.to_string()).or_default();21 host.encryption_key = key.trim().to_string();22 }23 pub fn update_secret(&self, host: &str, name: &str, value: &[u8]) {24 let mut data = self.data_mut();25 let host = data.hosts.entry(host.to_string()).or_default();26 host.encrypted_secrets.insert(27 name.to_string(),28 format!("[ENCRYPTED:{}]", base64::encode(value)),29 );30 }3132 pub fn key(&self, host: &str) -> anyhow::Result<String> {33 if let Some(key) = self.cached_key(host) {34 Ok(key)35 } else {36 warn!("Loading key for {}", host);37 let key = self38 .command_on("host", "cat", false)39 .arg("/etc/ssh/ssh_host_ed25519_key.pub")40 .run_string()?;41 self.update_key(host, key.clone());42 Ok(key)43 }44 }45 pub fn recipient(&self, host: &str) -> anyhow::Result<age::ssh::Recipient> {46 let key = self.key(host)?;47 age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e))48 }4950 pub fn orphaned_data(&self) -> Result<Vec<String>> {51 let mut out = Vec::new();52 let host_names = self.list_hosts()?;53 for hostname in self54 .data()55 .hosts56 .iter()57 .filter(|(_, host)| !host.encryption_key.is_empty())58 .map(|(n, _)| n)59 {60 if !host_names.contains(&hostname.to_owned()) {61 out.push(hostname.to_owned())62 }63 }6465 Ok(out)66 }67}