use std::path::Path;
use std::process::Command;
use super::config::SigningFormat;
#[allow(dead_code)] pub fn detect_signing_format(key: &str) -> SigningFormat {
if key.ends_with(".pub") {
return SigningFormat::Ssh;
}
let expanded = shellexpand::tilde(key);
let path = Path::new(expanded.as_ref());
if path.exists() && path.is_file() {
if let Ok(content) = std::fs::read_to_string(path) {
if content.starts_with("ssh-") || content.starts_with("ecdsa-") {
return SigningFormat::Ssh;
}
}
}
SigningFormat::Gpg
}
#[allow(dead_code)] pub fn validate_signing_key(key: &str, format: SigningFormat) -> Result<(), String> {
match format {
SigningFormat::Ssh => {
let expanded = shellexpand::tilde(key);
let path = Path::new(expanded.as_ref());
if !path.exists() {
return Err(format!("SSH key not found: {key}"));
}
if !path.is_file() {
return Err(format!("SSH key is not a file: {key}"));
}
Ok(())
}
SigningFormat::Gpg => {
let output = Command::new("gpg")
.args(["--list-secret-keys", key])
.output();
match output {
Ok(o) if o.status.success() => Ok(()),
Ok(_) => Err(format!("GPG key not found: {key}")),
Err(e) => Err(format!("Failed to check GPG key: {e}")),
}
}
}
}
#[allow(dead_code)] pub fn ssh_allowed_signers_path() -> String {
let home = dirs::home_dir().map(|p| p.to_string_lossy().to_string());
match home {
Some(h) => format!("{h}/.ssh/allowed_signers"),
None => "~/.ssh/allowed_signers".to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_signing_format_ssh() {
assert_eq!(
detect_signing_format("~/.ssh/id_ed25519.pub"),
SigningFormat::Ssh
);
assert_eq!(
detect_signing_format("/home/user/.ssh/id_rsa.pub"),
SigningFormat::Ssh
);
}
#[test]
fn test_detect_signing_format_gpg() {
assert_eq!(detect_signing_format("ABC123DEF456"), SigningFormat::Gpg);
assert_eq!(
detect_signing_format("user@example.com"),
SigningFormat::Gpg
);
}
#[test]
fn test_ssh_allowed_signers_path() {
let path = ssh_allowed_signers_path();
assert!(path.ends_with("allowed_signers"));
}
}