163 lines
5.8 KiB
Rust
163 lines
5.8 KiB
Rust
use std::cmp::Ordering;
|
|
use std::collections::HashMap;
|
|
use std::fs;
|
|
|
|
use rust_util::{debugging, opt_result, simple_error, XResult};
|
|
use rust_util::util_file::resolve_file_path;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::spec::TinyEncryptEnvelopType;
|
|
|
|
/// Config file sample:
|
|
/// ~/.tinyencrypt/config-rs.json
|
|
/// {
|
|
/// "envelops": [
|
|
/// {
|
|
/// "type": "pgp",
|
|
/// "kid": "KID-1",
|
|
/// "desc": "this is key 001",
|
|
/// "publicPart": "----- BEGIN OPENPGP ..."
|
|
/// },
|
|
/// {
|
|
/// "type": "ecdh",
|
|
/// "sid": "SHORT-ID-1",
|
|
/// "kid": "KID-2",
|
|
/// "desc": "this is key 002",
|
|
/// "publicPart": "04..."
|
|
/// }
|
|
/// ],
|
|
/// "profiles": {
|
|
/// "default": ["KID-1", "KID-2", "type:pgp"],
|
|
/// "leve2": ["KID-2"]
|
|
/// }
|
|
/// }
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct TinyEncryptConfig {
|
|
pub envelops: Vec<TinyEncryptConfigEnvelop>,
|
|
pub profiles: HashMap<String, Vec<String>>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct TinyEncryptConfigEnvelop {
|
|
pub r#type: TinyEncryptEnvelopType,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub sid: Option<String>,
|
|
pub kid: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub desc: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub args: Option<Vec<String>>,
|
|
pub public_part: String,
|
|
}
|
|
|
|
impl TinyEncryptConfig {
|
|
pub fn load(file: &str) -> XResult<Self> {
|
|
let resolved_file = resolve_file_path(file);
|
|
let config_contents = opt_result!(
|
|
fs::read_to_string(resolved_file), "Read config file: {}, failed: {}", file
|
|
);
|
|
let mut config: TinyEncryptConfig = opt_result!(
|
|
serde_json::from_str(&config_contents),"Parse config file: {}, failed: {}", file);
|
|
let mut splited_profiles = HashMap::new();
|
|
for (k, v) in config.profiles.into_iter() {
|
|
if !k.contains(',') {
|
|
splited_profiles.insert(k, v);
|
|
} else {
|
|
k.split(',')
|
|
.map(|k| k.trim())
|
|
.filter(|k| !k.is_empty())
|
|
.for_each(|k| {
|
|
splited_profiles.insert(k.to_string(), v.clone());
|
|
});
|
|
}
|
|
}
|
|
config.profiles = splited_profiles;
|
|
Ok(config)
|
|
}
|
|
|
|
pub fn find_first_arg_by_kid(&self, kid: &str) -> Option<&String> {
|
|
self.find_args_by_kid(kid).and_then(|a| a.iter().next())
|
|
}
|
|
|
|
pub fn find_args_by_kid(&self, kid: &str) -> Option<&Vec<String>> {
|
|
self.find_by_kid(kid).and_then(|e| e.args.as_ref())
|
|
}
|
|
|
|
pub fn find_by_kid(&self, kid: &str) -> Option<&TinyEncryptConfigEnvelop> {
|
|
self.find_by_kid_or_filter(kid, |_| false).first().copied()
|
|
}
|
|
|
|
pub fn find_by_kid_or_type(&self, k_filter: &str) -> Vec<&TinyEncryptConfigEnvelop> {
|
|
self.find_by_kid_or_filter(k_filter, |e| {
|
|
let envelop_type = format!("type:{}", &e.r#type.get_name());
|
|
if k_filter == "ALL" || k_filter == "*" || k_filter == envelop_type {
|
|
return true;
|
|
}
|
|
if k_filter.ends_with('*') {
|
|
let new_k_filter = k_filter.chars().collect::<Vec<_>>();
|
|
let new_k_filter = new_k_filter.iter().take(new_k_filter.len() - 1).collect::<String>();
|
|
if e.kid.starts_with(&new_k_filter) || envelop_type.starts_with(&new_k_filter) {
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
})
|
|
}
|
|
|
|
pub fn find_by_kid_or_filter<F>(&self, kid: &str, f: F) -> Vec<&TinyEncryptConfigEnvelop>
|
|
where F: Fn(&TinyEncryptConfigEnvelop) -> bool {
|
|
self.envelops.iter().filter(|e| {
|
|
if e.kid == kid { return true; }
|
|
if let Some(sid) = &e.sid {
|
|
if sid == kid { return true; }
|
|
}
|
|
f(e)
|
|
}).collect()
|
|
}
|
|
|
|
pub fn find_envelops(&self, profile: &Option<String>, key_filter: &Option<String>)
|
|
-> XResult<Vec<&TinyEncryptConfigEnvelop>> {
|
|
debugging!("Profile: {:?}", profile);
|
|
debugging!("Key filter: {:?}", key_filter);
|
|
let mut matched_envelops_map = HashMap::new();
|
|
let mut key_ids = vec![];
|
|
if key_filter.is_none() || profile.is_some() {
|
|
let profile = profile.as_ref().map(String::as_str).unwrap_or("default");
|
|
if let Some(kids) = self.profiles.get(profile) {
|
|
kids.iter().for_each(|k| key_ids.push(k.to_string()));
|
|
}
|
|
}
|
|
if let Some(key_filter) = key_filter {
|
|
key_filter.split(',').for_each(|k| {
|
|
let k = k.trim();
|
|
if !k.is_empty() {
|
|
key_ids.push(k.to_string());
|
|
}
|
|
});
|
|
}
|
|
if key_ids.is_empty() {
|
|
return simple_error!("Profile or key filter cannot find any valid envelopes");
|
|
}
|
|
for key_id in &key_ids {
|
|
for envelop in self.find_by_kid_or_type(key_id) {
|
|
matched_envelops_map.insert(&envelop.kid, envelop);
|
|
}
|
|
}
|
|
|
|
let mut envelops: Vec<_> = matched_envelops_map.values().copied().collect();
|
|
if envelops.is_empty() {
|
|
return simple_error!("Profile or key filter cannot find any valid envelopes");
|
|
}
|
|
envelops.sort_by(|e1, e2| {
|
|
if e1.r#type < e2.r#type { return Ordering::Greater; }
|
|
if e1.r#type > e2.r#type { return Ordering::Less; }
|
|
if e1.kid < e2.kid { return Ordering::Greater; }
|
|
if e1.kid > e2.kid { return Ordering::Less; }
|
|
Ordering::Equal
|
|
});
|
|
Ok(envelops)
|
|
}
|
|
}
|