This commit is contained in:
2026-05-10 19:42:47 -05:00
parent e4a3eaae08
commit bfd4814d70
6 changed files with 317 additions and 7 deletions
+61 -6
View File
@@ -6,6 +6,7 @@ use flate2::Compression;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use clap::{Parser, Subcommand};
use ssh_key::{Algorithm, LineEnding, PrivateKey};
use crate::error::DrawerError;
use crate::parsing::KeyType::*;
@@ -13,6 +14,7 @@ use crate::parsing::KeyType::*;
pub enum OperationType {
Open,
Close,
Key,
}
#[derive(Debug, PartialEq)]
@@ -22,6 +24,7 @@ pub struct DrawerOperation {
pub key: KeyType,
pub target_path: PathBuf,
pub force: bool,
pub target_inferred: bool,
}
#[derive(Debug, PartialEq)]
@@ -52,6 +55,9 @@ enum Command {
#[arg(short = 'i')]
key: Option<PathBuf>,
},
Key {
filename: PathBuf,
},
}
pub fn parse_args<I, T>(args: I) -> Result<DrawerOperation, DrawerError>
@@ -65,15 +71,17 @@ where
match cli.command {
Command::Open { drawer_file, target_path, key, force } => {
let key = resolve_key(key);
let target = target_path.unwrap_or_else(|| {
PathBuf::from(drawer_file.file_stem().unwrap_or_default())
});
let (target, target_inferred) = match target_path {
Some(p) => (p, false),
None => (PathBuf::from(drawer_file.file_stem().unwrap_or_default()), true),
};
Ok(DrawerOperation {
operation_type: OperationType::Open,
drawer_file,
key,
target_path: target,
force,
target_inferred,
})
}
Command::Close { drawer_file, target_path, key } => {
@@ -85,6 +93,17 @@ where
key,
target_path: target,
force: false,
target_inferred: false,
})
}
Command::Key { filename } => {
Ok(DrawerOperation {
operation_type: OperationType::Key,
drawer_file: filename,
key: KeyType::EnvVar,
target_path: PathBuf::from("."),
force: false,
target_inferred: false,
})
}
}
@@ -103,10 +122,14 @@ impl DrawerOperation {
match self.operation_type {
OperationType::Open => self.perform_open(),
OperationType::Close => self.perform_close(),
OperationType::Key => self.perform_key(),
}
}
fn perform_open(&self) -> Result<(), DrawerError> {
if self.target_inferred {
println!("(assuming into {})", self.target_path.display());
}
let encrypted = std::fs::read(&self.drawer_file)
.map_err(|_| DrawerError::DrawerFileNotFound(self.drawer_file.clone()))?;
@@ -179,16 +202,39 @@ impl DrawerOperation {
}
pub fn validate(&self) -> Result<(), DrawerError> {
if self.drawer_file.extension().and_then(|e| e.to_str()) != Some("drawer") {
return Err(DrawerError::DrawerFileInvalidExtension(self.drawer_file.clone()));
if self.operation_type != OperationType::Key {
if self.drawer_file.extension().and_then(|e| e.to_str()) != Some("drawer") {
return Err(DrawerError::DrawerFileInvalidExtension(self.drawer_file.clone()));
}
self.validate_key()?;
}
self.validate_key()?;
match self.operation_type {
OperationType::Open => self.validate_open(),
OperationType::Close => self.validate_close(),
OperationType::Key => self.validate_key_gen(),
}
}
fn validate_key_gen(&self) -> Result<(), DrawerError> {
if self.drawer_file.exists() {
return Err(DrawerError::KeyOutputFailed(self.drawer_file.clone()));
}
let parent = match self.drawer_file.parent() {
None => Path::new("."),
Some(p) => {
if p == Path::new("") {
Path::new(".")
} else {
p
}
},
};
if !parent.exists() {
return Err(DrawerError::KeyOutputFailed(self.drawer_file.clone()));
}
Ok(())
}
fn key_path(&self) -> Result<PathBuf, DrawerError> {
match &self.key {
EnvVar => {
@@ -225,6 +271,15 @@ impl DrawerOperation {
Ok(())
}
fn perform_key(&self) -> Result<(), DrawerError> {
let private_key = PrivateKey::random(&mut rand::rngs::OsRng, Algorithm::Ed25519)
.map_err(|_| DrawerError::KeyGenerateFailed)?;
let pem = private_key.to_openssh(LineEnding::LF)
.map_err(|_| DrawerError::KeyGenerateFailed)?;
std::fs::write(&self.drawer_file, pem.as_bytes())
.map_err(|_| DrawerError::KeyOutputFailed(self.drawer_file.clone()))
}
fn validate_close(&self) -> Result<(), DrawerError> {
let meta = std::fs::metadata(&self.target_path)
.map_err(|_| DrawerError::TargetInvalid(self.target_path.clone()))?;