colmena/src/nix/key.rs

94 lines
2.3 KiB
Rust
Raw Normal View History

2021-02-10 20:22:31 +02:00
use std::{
io::{self, Cursor},
2021-02-10 17:34:52 -08:00
path::{Path, PathBuf},
process::Stdio,
2021-02-10 20:22:31 +02:00
};
use regex::Regex;
2021-02-10 20:22:31 +02:00
use serde::{Deserialize, Serialize};
use tokio::{
fs::File,
io::AsyncRead,
process::Command,
};
use validator::{Validate, ValidationError};
2021-02-10 17:34:52 -08:00
#[derive(Debug, Clone, Serialize, Deserialize)]
enum KeySource {
#[serde(rename = "text")]
Text(String),
#[serde(rename = "keyCommand")]
Command(Vec<String>),
#[serde(rename = "keyFile")]
File(PathBuf),
}
#[derive(Debug, Clone, Validate, Serialize, Deserialize)]
pub struct Key {
2021-02-10 17:34:52 -08:00
#[serde(flatten)]
source: KeySource,
#[validate(custom = "validate_dest_dir")]
#[serde(rename = "destDir")]
2021-02-10 17:34:52 -08:00
dest_dir: PathBuf,
#[validate(custom = "validate_unix_name")]
2021-02-10 17:34:52 -08:00
user: String,
#[validate(custom = "validate_unix_name")]
2021-02-10 17:34:52 -08:00
group: String,
permissions: String,
}
2021-02-10 20:22:31 +02:00
impl Key {
2021-02-10 17:34:52 -08:00
pub async fn reader(&'_ self,) -> Result<Box<dyn AsyncRead + Send + Unpin + '_>, io::Error> {
match &self.source {
KeySource::Text(content) => {
Ok(Box::new(Cursor::new(content)))
}
KeySource::Command(command) => {
let pathname = &command[0];
let argv = &command[1..];
let stdout = Command::new(pathname)
.args(argv)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?
.stdout.take().unwrap();
Ok(Box::new(stdout))
2021-02-10 17:34:52 -08:00
}
KeySource::File(path) => {
Ok(Box::new(File::open(path).await?))
}
2021-02-10 20:22:31 +02:00
}
}
2021-02-10 17:34:52 -08:00
pub fn dest_dir(&self) -> &Path { &self.dest_dir }
pub fn user(&self) -> &str { &self.user }
pub fn group(&self) -> &str { &self.user }
pub fn permissions(&self) -> &str { &self.permissions }
2021-02-10 20:22:31 +02:00
}
fn validate_unix_name(name: &str) -> Result<(), ValidationError> {
let re = Regex::new(r"^[a-z][-a-z0-9]*$").unwrap();
if re.is_match(name) {
Ok(())
} else {
Err(ValidationError::new("Invalid user/group name"))
}
}
fn validate_dest_dir(dir: &PathBuf) -> Result<(), ValidationError> {
if dir.has_root() {
Ok(())
} else {
Err(ValidationError::new("Secret key destination directory must be absolute"))
}
}