chore: add src
This commit is contained in:
parent
a7f10ba503
commit
e3a23eeaf9
60
src/crypt_lib.rs
Normal file
60
src/crypt_lib.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use crate::utils::{trim_newline};
|
||||
/// encrypt & decrypt content with `enc_file` using `XChaCha20Poly1305` and random nonce
|
||||
///```rust
|
||||
/// let texto = String::from("Esto es una prueba");
|
||||
/// println!("{}",&texto);
|
||||
/// let ky = String::from("an example very very secret key.");
|
||||
/// let key= ky.as_str(); //Plaintext to encrypt
|
||||
/// let res_enc = encrypt(&texto,key);
|
||||
/// println!("encripty: {}",&res_enc);
|
||||
/// let res_dec = decrypt(&res_enc,key);
|
||||
/// println!("decrypt: {}",&res_dec);
|
||||
///```
|
||||
#[must_use]
|
||||
pub fn encrypt(text: &str, key: &str) -> String {
|
||||
if key.is_empty() {
|
||||
return text.to_string();
|
||||
}
|
||||
//let txt = format!(r#"{}"#, text); //Plaintext to encrypt
|
||||
let text_vec = text.as_bytes().to_vec();
|
||||
//let key: &str = "an example very very secret key."; //Key will normally be chosen from keymap and provided to the encrypt_chacha() function
|
||||
//let text_vec: Vec<u8> = txt_char.to_vec(); //Convert text to Vec<u8>
|
||||
//Ciphertext stores the len() of encrypted content, the nonce and the actual ciphertext using bincode
|
||||
match enc_file::encrypt_chacha(text_vec, key) {
|
||||
//encrypt vec<u8>, returns result(Vec<u8>)
|
||||
Ok(ciphertext) => base64::encode(&ciphertext),
|
||||
Err(e) => {
|
||||
println!("Error encrypt: {}", e);
|
||||
String::from("")
|
||||
}
|
||||
}
|
||||
}
|
||||
/// decrypt content with `enc_file` using `XChaCha20Poly1305` and random nonce
|
||||
#[must_use]
|
||||
pub fn decrypt(text: &str, key: &str) -> String {
|
||||
if key.is_empty() {
|
||||
return text.to_string();
|
||||
}
|
||||
let mut input = text.to_owned();
|
||||
trim_newline(&mut input);
|
||||
match base64::decode(&input) {
|
||||
Ok(inpt_text) => match enc_file::decrypt_chacha(inpt_text, key) {
|
||||
Ok(res) => match String::from_utf8(res) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
println!("Error utf8 decode: {}", e);
|
||||
String::from("")
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error decrypt: {}", e);
|
||||
String::from("")
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error base64 decode: {}", e);
|
||||
String::from("")
|
||||
}
|
||||
}
|
||||
// println!("text: {} plain: {}",format!("{:?}", &text), format!("{:?}", plaintext)); //Check that text == plaintext
|
||||
}
|
12
src/lib.rs
Normal file
12
src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
||||
/*! tkdr functions for variety of tasks */
|
||||
//
|
||||
// Copyright 2020, Jesús Pérez Lorenzo
|
||||
|
||||
/// utilities
|
||||
pub mod utils;
|
||||
/// Tera utilities
|
||||
pub mod tera_lib;
|
||||
/// Crypto & Decrypt
|
||||
pub mod crypt_lib;
|
||||
/// Random key
|
||||
pub mod randomkey;
|
127
src/randomkey.rs
Normal file
127
src/randomkey.rs
Normal file
@ -0,0 +1,127 @@
|
||||
/// Random keygen form [rusty-keys](https://github.com/rideron89/rusty-keys)
|
||||
///```rust
|
||||
/// let random = RandomKey::default();
|
||||
/// let tkn0 = random.from_template("uuid",1)[0];
|
||||
/// let tkn1 = random.get_key();
|
||||
/// let tkn = format!("{}\n{}\n",tkn0,&tkn1);
|
||||
///```
|
||||
/// `RandomKey` can be configured from Config (config.toml) with values for
|
||||
/// - length: `config.auth.keys_length`,
|
||||
/// - `alfanumeric_only`: `config.auth.keys_alfanumeric`,
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RandomKey {
|
||||
count: u32,
|
||||
length: u32,
|
||||
alfanumeric_only: bool,
|
||||
}
|
||||
impl Default for RandomKey {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
count: 1,
|
||||
length: 16,
|
||||
alfanumeric_only: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl RandomKey {
|
||||
#[allow(clippy::missing_docs_in_private_items)]
|
||||
/// Configure `RandomKey`
|
||||
#[must_use]
|
||||
pub fn config(length: u32, alfanumeric_only: bool) -> Self {
|
||||
Self {
|
||||
count: 1,
|
||||
length,
|
||||
alfanumeric_only,
|
||||
}
|
||||
}
|
||||
#[allow(clippy::missing_docs_in_private_items)]
|
||||
/// Get a key
|
||||
#[must_use]
|
||||
pub fn get_key(&self) -> String {
|
||||
match self
|
||||
.get_list(self.length, self.count, self.alfanumeric_only)
|
||||
.get(0)
|
||||
{
|
||||
Some(key) => key.to_owned(),
|
||||
None => String::from(""),
|
||||
}
|
||||
}
|
||||
/// Get a list of keys
|
||||
#[must_use]
|
||||
#[allow(
|
||||
clippy::missing_docs_in_private_items,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_possible_truncation
|
||||
)]
|
||||
pub fn get_list(&self, length: u32, count: u32, allow_symbols: bool) -> Vec<String> {
|
||||
let mut alphabet =
|
||||
String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ0123456789");
|
||||
|
||||
if allow_symbols {
|
||||
alphabet.push_str("/.;!@$");
|
||||
}
|
||||
let mut codes: Vec<String> = Vec::new();
|
||||
for _ in 0..count {
|
||||
let mut code = String::new();
|
||||
|
||||
for _ in 0..length {
|
||||
let number = rand::random::<f32>() * (alphabet.len() as f32);
|
||||
let number = number.round() as usize;
|
||||
if let Some(character) = alphabet.chars().nth(number) {
|
||||
code.push(character)
|
||||
}
|
||||
}
|
||||
codes.push(code);
|
||||
}
|
||||
codes
|
||||
}
|
||||
/// Print a list of passwords for a given template.
|
||||
#[must_use]
|
||||
pub fn from_template(&self, template: &str, count: u32) -> Vec<String> {
|
||||
let mut codes: Vec<String> = Vec::new();
|
||||
for _ in 0..count {
|
||||
match template {
|
||||
"uuid" => codes.push(self.generate_uuid()),
|
||||
&_ => {
|
||||
// the process should never get to this point, because we are checking
|
||||
// against valid templates in a previous step
|
||||
eprintln!("'{}' is an invalid template", template);
|
||||
}
|
||||
}
|
||||
}
|
||||
codes
|
||||
}
|
||||
/// Generate a UUID key.
|
||||
/// Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
/// Example: c17247f6-55b6-9226-28fc-952b7becc6e9
|
||||
#[must_use]
|
||||
#[allow(
|
||||
clippy::unnecessary_cast,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_possible_truncation
|
||||
)]
|
||||
pub fn generate_uuid(&self) -> String {
|
||||
let alphabet = String::from("abcdef0123456789");
|
||||
|
||||
let mut code = String::new();
|
||||
|
||||
for _ in 0..32 {
|
||||
// 16 is the length of the above Hex alphabet
|
||||
let number = rand::random::<f32>() * (16 as f32);
|
||||
let number = number.round() as usize;
|
||||
|
||||
if let Some(character) = alphabet.chars().nth(number) {
|
||||
code.push(character)
|
||||
}
|
||||
}
|
||||
|
||||
code.insert(20, '-');
|
||||
code.insert(16, '-');
|
||||
code.insert(12, '-');
|
||||
code.insert(8, '-');
|
||||
|
||||
code
|
||||
}
|
||||
}
|
231
src/tera_lib.rs
Normal file
231
src/tera_lib.rs
Normal file
@ -0,0 +1,231 @@
|
||||
/*! Utility functions for variety of tasks */
|
||||
//
|
||||
// Copyright 2020, Jesús Pérez Lorenzo
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tera::{Tera};
|
||||
|
||||
/// make content from template path: from `root_path` + extension search for ".toml" or ".json"
|
||||
/// template should be `root_path`.html and lang should be included in `root_path`
|
||||
#[must_use]
|
||||
#[allow(clippy::implicit_hasher)]
|
||||
pub fn make_template_content(
|
||||
root_path: &str,
|
||||
ctx: &mut tera::Context,
|
||||
data_hash: &mut HashMap<String, String>,
|
||||
load_extend: bool,
|
||||
) -> String {
|
||||
let mut data_path = format!("{}.toml", &root_path);
|
||||
if !Path::new(&data_path).exists() {
|
||||
data_path = format!("{}.json", &root_path);
|
||||
}
|
||||
match hash_from_data(&data_path,ctx,data_hash,load_extend) {
|
||||
Ok(_) => match data_templated(
|
||||
"",
|
||||
format!("{}.html", &root_path).as_str(),
|
||||
ctx,
|
||||
data_hash,
|
||||
) {
|
||||
Ok(templated_file) => return templated_file,
|
||||
Err(e) => println!(
|
||||
"Error generating template {}.html: {}",
|
||||
&root_path, e
|
||||
),
|
||||
},
|
||||
Err(e) => println!(
|
||||
"Error with data template {} - {} : {}",
|
||||
&root_path, &data_path, e
|
||||
),
|
||||
}
|
||||
"".to_string()
|
||||
}
|
||||
/// `get_json_val` is a simple match Option to remove quotes in json values and returned as a String
|
||||
#[must_use]
|
||||
pub fn get_json_val(json_value: &serde_json::Value, dflt_val: String) -> String {
|
||||
match json_value.as_str() {
|
||||
Some(val) => val.to_string(),
|
||||
None => dflt_val,
|
||||
}
|
||||
}
|
||||
/// `get_toml_val` is a simple match Option to remove quotes in json values and returned as a String
|
||||
#[must_use]
|
||||
pub fn get_toml_val(toml_value: &toml::Value, dflt_val: String) -> String {
|
||||
match toml_value.as_str() {
|
||||
Some(val) => val.to_string(),
|
||||
None => dflt_val,
|
||||
}
|
||||
}
|
||||
/// `get_toml_val` is a simple match Option to remove quotes in json values and returned as a String
|
||||
#[must_use]
|
||||
pub fn get_yaml_val(yaml_value: &serde_yaml::Value, dflt_val: String) -> String {
|
||||
match yaml_value.as_str() {
|
||||
Some(val) => val.to_string(),
|
||||
None => dflt_val,
|
||||
}
|
||||
}
|
||||
/// Load a data file, fill a receive HASH, load template file and applied with Tera
|
||||
/// By file extension is able to load data from JSON or TOML (as a table Value)
|
||||
///
|
||||
/// Unwrap lines in the following example has to be changed to "matched" or ? with -> Result<>
|
||||
///```rust
|
||||
/// let mut data_hash: HashMap<String, String> = HashMap::new();
|
||||
/// let path = String::from("resources/mail/test.json".to_string());
|
||||
/// let res = hash_from_data(&data_path, &mut data_hash).unwrap();
|
||||
/// let root_path = String::from("resources/mail");
|
||||
/// let tpl_result = data_templated(&root_path,"test.html",&res).unwrap();
|
||||
/// ```
|
||||
#[allow(clippy::missing_errors_doc,clippy::implicit_hasher)]
|
||||
|
||||
|
||||
//
|
||||
pub fn hash_from_data(path: &str, ctx: &mut tera::Context, data_hash: &mut HashMap<String, String>, load_extend: bool) -> anyhow::Result<()> {
|
||||
// {HashMap<String, String>> {
|
||||
let mut check_value = | key: String, v: String | {
|
||||
if let Some(first_char) = v.chars().next() {
|
||||
if first_char == '@' {
|
||||
let field_file = &v[1..];
|
||||
let _ = hash_from_data(field_file, ctx, data_hash, load_extend);
|
||||
// dbg!("{}",&data_hash);
|
||||
return;
|
||||
}
|
||||
}
|
||||
data_hash.insert(key, v);
|
||||
};
|
||||
let vars_content = fs::read_to_string(&path)?;
|
||||
//let mut data_hash: HashMap<String, String> = HashMap::new();
|
||||
if let Some(extension) = path.split('.').last() {
|
||||
//println!("File {} extension {}", &path, &extension);
|
||||
match extension {
|
||||
"json" => {
|
||||
let data: HashMap<String, serde_json::Value> = serde_json::from_str(&vars_content)?;
|
||||
if load_extend {
|
||||
match tera::Context::from_serialize(data) {
|
||||
Ok(res) => {
|
||||
ctx.extend(res);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error loading {}: {}",&path, e);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
for (key, value) in data {
|
||||
let val = get_json_val(&value, "".into());
|
||||
check_value(key,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
"toml" => {
|
||||
// As a table content
|
||||
let data: toml::Value = toml::from_str(&vars_content)?;
|
||||
// let data = &vars_content.parse::<toml::Value>()?;
|
||||
if load_extend {
|
||||
match tera::Context::from_serialize(data) {
|
||||
Ok(res) => {
|
||||
ctx.extend(res);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error loading {}: {}",&path, e);
|
||||
},
|
||||
}
|
||||
} else if let Some(table_data) = data.as_table() {
|
||||
for (key, value) in table_data {
|
||||
println!("-{}-{}",&key,&value);
|
||||
let val = get_toml_val(&value, "".into());
|
||||
check_value(key.into(),val);
|
||||
}
|
||||
}
|
||||
}
|
||||
"yaml" => {
|
||||
let data: HashMap<String, serde_yaml::Value> = serde_yaml::from_str(&vars_content)?;
|
||||
if load_extend {
|
||||
match tera::Context::from_serialize(data) {
|
||||
Ok(res) => {
|
||||
ctx.extend(res);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error loading {}: {}",&path, e);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
for (key, value) in data {
|
||||
let val = get_yaml_val(&value, "".into());
|
||||
check_value(key,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
"File {} format extension {} not defined ",
|
||||
&path, &extension
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("File {} unknown extension", &path);
|
||||
}
|
||||
// dbg!(&data_hash);
|
||||
Ok(()) // data_hash)
|
||||
}
|
||||
/// Data tempalted create a Tera context form hashmap and reads a file template from path/template_name
|
||||
/// Finally render to a String
|
||||
#[allow(clippy::missing_errors_doc, clippy::implicit_hasher)]
|
||||
pub fn data_templated(
|
||||
path: &str,
|
||||
template_name: &str,
|
||||
tpl_context: &mut tera::Context,
|
||||
content: &HashMap<String, String>,
|
||||
) -> anyhow::Result<String> {
|
||||
// let mut tpl_context = Context::new();
|
||||
for (key, value) in content.iter() {
|
||||
tpl_context.insert(key, value);
|
||||
}
|
||||
let mut tera = Tera::default();
|
||||
|
||||
let template_head = "header.html";
|
||||
let tpl = read_path_file(&path, &template_name, "content")?;
|
||||
let mut all_tpls = vec![("data-template.html",tpl)];
|
||||
match read_path_file(&path, &template_head, "content") {
|
||||
Ok(tpl_head) => all_tpls.push((&template_head,tpl_head)),
|
||||
Err(_) => {} // ignore if no header
|
||||
}
|
||||
tera.add_raw_templates(all_tpls)?;
|
||||
let res = tera.render("data-template.html", &tpl_context)?;
|
||||
Ok(res)
|
||||
}
|
||||
/// Read a `file_name` from a path (root / `file_name`) ( "" `file_name_full_path` )
|
||||
/// format of reading can be matched with `file_type`
|
||||
/// TODO implement other types to read
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub fn read_path_file(path: &str, file_name: &str, file_type: &str) -> anyhow::Result<String> {
|
||||
let mut result = String::from("");
|
||||
let mut root_path = format!("{}/", path);
|
||||
if root_path.as_str() == "/" {
|
||||
root_path = String::from("");
|
||||
}
|
||||
//println!("{}",format!("{}{}", &root_path.as_str(), &file_name));
|
||||
match file_type {
|
||||
"content" => {
|
||||
result = fs::read_to_string(format!("{}{}", &root_path.as_str(), &file_name).as_str())?
|
||||
}
|
||||
_ => println!(
|
||||
"File {}{} type {} not defined ",
|
||||
&root_path, &file_name, &file_type
|
||||
),
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
/// `hash_content` to hash content with blake3:hash
|
||||
///```rust
|
||||
/// let texto = String::from("Esto es una prueba");
|
||||
/// println!("{}",&texto);
|
||||
/// println!("hash: {}",hash_content(&texto));
|
||||
///```
|
||||
#[must_use]
|
||||
pub fn hash_content(content: &str) -> String {
|
||||
let cntnt = content.as_bytes().to_vec(); //std::fmt::Binary(content.as_str());
|
||||
// blake3::hash(b"foobarbaz");
|
||||
let hash = blake3::hash(&cntnt);
|
||||
format!("{}", hash.to_hex())
|
||||
}
|
123
src/utils.rs
Normal file
123
src/utils.rs
Normal file
@ -0,0 +1,123 @@
|
||||
/*! Utility functions for variety of tasks */
|
||||
//
|
||||
// Copyright 2020, Jesús Pérez Lorenzo
|
||||
|
||||
use anyhow::anyhow;
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(PartialEq, Default, Clone, Debug)]
|
||||
struct Commit {
|
||||
hash: String,
|
||||
message: String,
|
||||
}
|
||||
/// A very simple Operating System Command runner capturing output
|
||||
/// Allows two arguments, they can be empty
|
||||
///```rust
|
||||
/// let res =
|
||||
/// match run_command("pwd", "", "") {
|
||||
/// Ok(_) => format!("{} {}", "pwd","Done"),
|
||||
/// Err(e) => format!("{}",e),
|
||||
/// };
|
||||
/// println!("Result: {}",res);
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
#[allow(clippy::missing_errors_doc, clippy::filter_map)]
|
||||
pub fn run_command(cmd: &str, arg1: &str, arg2: &str) -> anyhow::Result<()> {
|
||||
let output = match arg2 {
|
||||
"" => match arg1 {
|
||||
"" => Command::new(cmd).output()?,
|
||||
_ => Command::new(cmd).arg(arg1).output()?,
|
||||
},
|
||||
_ => Command::new(cmd).arg(arg1).arg(arg2).output()?,
|
||||
};
|
||||
|
||||
if !output.status.success() {
|
||||
//dbg!(&output);
|
||||
let code = match output.status.code() {
|
||||
Some(val) => val,
|
||||
None => -1,
|
||||
};
|
||||
let stderr = String::from_utf8(output.stderr)?;
|
||||
let msg = format!(
|
||||
"Command {} executed with failing errors({}): {}",
|
||||
&cmd, &code, &stderr
|
||||
);
|
||||
println!("{}", &msg);
|
||||
return Err(anyhow!(msg));
|
||||
// error_chain::bail!("Command {} executed with failing error code", &cmd);
|
||||
}
|
||||
|
||||
let pattern = Regex::new(
|
||||
r"(?x)
|
||||
([0-9a-fA-F]+) # commit hash
|
||||
(.*) # The commit message",
|
||||
)?;
|
||||
String::from_utf8(output.stdout)?
|
||||
.lines()
|
||||
.filter_map(|line| pattern.captures(line))
|
||||
.map(|cap| Commit {
|
||||
hash: cap[1].to_string(),
|
||||
message: cap[2].trim().to_string(),
|
||||
})
|
||||
.take(5)
|
||||
.for_each(|x| println!("{:?}", x));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Trim newline
|
||||
/// this function is critical for base64 content as usually adds a newline at the end, causing decode not work properly with code.
|
||||
/// `trim_newline` should be called to clean content before to be decoded.
|
||||
pub fn trim_newline(s: &mut String) {
|
||||
if s.ends_with('\n') {
|
||||
s.pop();
|
||||
if s.ends_with('\r') {
|
||||
s.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Get last char from String
|
||||
#[must_use]
|
||||
pub fn get_last_char_num(text: &str) -> u32 {
|
||||
let v_last: Vec<char> = text.chars().rev().take(1).collect();
|
||||
let s_last: String = v_last.iter().collect();
|
||||
let mut num = 0;
|
||||
match s_last.parse::<u32>() {
|
||||
Ok(n) => num = n,
|
||||
Err(e) => println!("Error last_char_num: {}", e),
|
||||
}
|
||||
num
|
||||
}
|
||||
/// Check file permissions
|
||||
/// Initial idea was capture file mode as 600 but permissions for unix in rust something else has to be done
|
||||
/// ```rust
|
||||
/// #[cfg(target_family = "unix")]
|
||||
/// use std::os::unix::fs::PermissionsExt
|
||||
/// ```
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub fn check_file_permissions(path: &str) -> anyhow::Result<bool> {
|
||||
// let info = os_info::get();
|
||||
// // Print full information:
|
||||
// println!("OS information: {}", info);
|
||||
|
||||
// // Print information separately:
|
||||
// println!("Type: {}", info.os_type());
|
||||
// println!("Version: {}", info.version());
|
||||
// println!("Bitness: {}", info.bitness());
|
||||
// let os = os_type::current_platform();
|
||||
// let macos = os_type::OSType::OSX;
|
||||
// match os_type::current_platform().os_type {
|
||||
// os_type::OSType::OSX => Ok(true),
|
||||
// _ => {
|
||||
// let perm = fs::File::open(path)?.metadata()?.permissions();
|
||||
// Ok(perm.readonly())
|
||||
// }
|
||||
// }
|
||||
let perm = fs::File::open(path)?.metadata()?.permissions();
|
||||
// if ! perm.readonly() {
|
||||
// println!("Review permissions: {}", path);
|
||||
// }
|
||||
// Ok(true)
|
||||
Ok(perm.readonly())
|
||||
}
|
Loading…
Reference in New Issue
Block a user