2021-02-10 20:22:31 +02:00
|
|
|
use std::{
|
|
|
|
io::{self, Cursor},
|
2021-02-10 17:34:52 -08:00
|
|
|
path::{Path, PathBuf},
|
2021-02-10 18:08:47 -08:00
|
|
|
process::Stdio,
|
2021-02-10 20:22:31 +02:00
|
|
|
};
|
2021-02-08 18:38:14 -08:00
|
|
|
|
|
|
|
use regex::Regex;
|
2021-02-10 20:22:31 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-02-10 18:08:47 -08:00
|
|
|
use tokio::{
|
|
|
|
fs::File,
|
|
|
|
io::AsyncRead,
|
|
|
|
process::Command,
|
|
|
|
};
|
2021-02-08 18:38:14 -08:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
|
2021-02-08 18:38:14 -08:00
|
|
|
#[derive(Debug, Clone, Validate, Serialize, Deserialize)]
|
|
|
|
pub struct Key {
|
2021-02-10 17:34:52 -08:00
|
|
|
#[serde(flatten)]
|
|
|
|
source: KeySource,
|
|
|
|
|
2021-02-08 18:38:14 -08:00
|
|
|
#[validate(custom = "validate_dest_dir")]
|
|
|
|
#[serde(rename = "destDir")]
|
2021-02-10 17:34:52 -08:00
|
|
|
dest_dir: PathBuf,
|
|
|
|
|
2021-02-08 18:38:14 -08:00
|
|
|
#[validate(custom = "validate_unix_name")]
|
2021-02-10 17:34:52 -08:00
|
|
|
user: String,
|
|
|
|
|
2021-02-08 18:38:14 -08:00
|
|
|
#[validate(custom = "validate_unix_name")]
|
2021-02-10 17:34:52 -08:00
|
|
|
group: String,
|
|
|
|
|
|
|
|
permissions: String,
|
2021-02-08 18:38:14 -08:00
|
|
|
}
|
|
|
|
|
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)))
|
|
|
|
}
|
2021-02-10 18:08:47 -08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-02-08 18:38:14 -08: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"))
|
|
|
|
}
|
|
|
|
}
|