diff --git a/airlock_libs/Cargo.lock b/airlock_libs/Cargo.lock index 52dd2a2..66728f0 100644 --- a/airlock_libs/Cargo.lock +++ b/airlock_libs/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "airlock_libs" -version = "5.0.0" +version = "5.0.1" dependencies = [ "chrono", "indicatif", diff --git a/airlock_libs/Cargo.toml b/airlock_libs/Cargo.toml index 07ad133..5704114 100644 --- a/airlock_libs/Cargo.toml +++ b/airlock_libs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "airlock_libs" -version = "5.0.0" +version = "5.0.1" edition = "2024" [lib] diff --git a/airlock_libs/pyproject.toml b/airlock_libs/pyproject.toml index 23e08a3..9ceacbc 100644 --- a/airlock_libs/pyproject.toml +++ b/airlock_libs/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "airlock_libs" -version = "5.0.0" +version = "5.0.1" description = "Airlock Digital API Wrapper" readme = "README.md" license = { text = "AGPL-3.0-only" } diff --git a/airlock_libs/src/lib.rs b/airlock_libs/src/lib.rs index d305d0c..b991583 100644 --- a/airlock_libs/src/lib.rs +++ b/airlock_libs/src/lib.rs @@ -1,5 +1,7 @@ use pyo3::prelude::*; -mod services; +pub mod modules; +pub mod services; +pub mod prelude; #[pymodule] fn airlock_libs(py: Python<'_>, m: &Bound) -> PyResult<()> { m.add_function(wrap_pyfunction!(services::pull_policy_exec_histories, py)?)?; diff --git a/airlock_libs/src/modules/datatypes.rs b/airlock_libs/src/modules/datatypes.rs new file mode 100644 index 0000000..c27af30 --- /dev/null +++ b/airlock_libs/src/modules/datatypes.rs @@ -0,0 +1,112 @@ +use crate::prelude::*; +use crate::services::get_base_directory; +#[allow(non_snake_case)] +#[derive(Deserialize, Debug)] +pub struct TelemetryConfig { + pub TELEMETRY: bool, + pub TELEM_URL: Option, +} + +impl TelemetryConfig { + pub fn load() -> Self { + let cfg_path = get_base_directory().join("config\\user_config.json"); + if !cfg_path.exists() { + return Self { + TELEMETRY: false, + TELEM_URL: None, + }; + } + match fs::read_to_string(&cfg_path) { + Ok(contents) => serde_json::from_str::(&contents).unwrap_or(Self { + TELEMETRY: false, + TELEM_URL: None, + }), + Err(_) => Self { + TELEMETRY: false, + TELEM_URL: None, + }, + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct ApiResponse { + pub(crate) error: String, + pub(crate) response: ExecHistories, +} +#[derive(Debug, Deserialize, Serialize)] +pub struct ExecHistories { + pub(crate) exechistories: Vec, +} +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Group { + pub(crate) checkpoint: String, + #[serde(rename = "type")] + pub(crate) exectype: u8, + pub(crate) username: String, + pub(crate) hostname: String, + pub(crate) netdomain: String, + pub(crate) filename: String, + pub(crate) ppolicy: String, + pub(crate) policyname: String, + pub(crate) policyver: String, + pub(crate) commandline: String, + pub(crate) publisher: String, + pub(crate) pprocess: String, + pub(crate) gprocess: String, + pub(crate) sha256: String, + pub(crate) datetime: String, + pub(crate) md5: String, + pub(crate) sha128: String, + pub(crate) sha384: String, + pub(crate) sha512: String, + pub(crate) ip: String, + pub(crate) localip: String, +} + +pub enum ExtractedValues { + Headers(reqwest::header::HeaderMap), + BaseUrl(String), +} + +pub trait Converter { + fn convert(py: Python<'_>, py_self: &Py, extract_headers: bool) -> ExtractedValues; +} + +pub struct PyData; + +impl Converter for PyData { + fn convert(py: Python<'_>, py_self: &Py, extract_headers: bool) -> ExtractedValues { + if extract_headers { + let headers = py_self.getattr(py, "headers").unwrap().to_string(); + let headers_replace = headers.replace('\'', "\""); + let parsed: Value = serde_json::from_str(headers_replace.as_str()).unwrap(); + let mut header_map = HeaderMap::new(); + if let Some(obj) = parsed.as_object() { + for (_key, value) in obj { + if let Some(v) = value.as_str() { + let val = HeaderValue::from_str(v).unwrap(); + header_map.insert(HeaderName::from_str("X-APIKey").unwrap(), val); + } + } + } + ExtractedValues::Headers(header_map) + } else { + let base_url = py_self.getattr(py, "base_url").unwrap().to_string(); + ExtractedValues::BaseUrl(base_url) + } + } +} + +pub struct SkipBack; + +impl SkipBack { + pub fn find_checkpoint(days: i64) -> ObjectId { + let date_days_ago = Local::now() - Duration::days(days); + let timestamp = date_days_ago.timestamp() as u32; + let mut hex_timestamp = String::new(); + write!(&mut hex_timestamp, "{:08x}", timestamp).unwrap(); + let objectid_hex = format!("{}0000000000000000", hex_timestamp); + ObjectId::parse_str(&objectid_hex).expect("Invalid ObjectId hex") + } +} \ No newline at end of file diff --git a/airlock_libs/src/modules/mod.rs b/airlock_libs/src/modules/mod.rs new file mode 100644 index 0000000..58fd615 --- /dev/null +++ b/airlock_libs/src/modules/mod.rs @@ -0,0 +1 @@ +pub mod datatypes; \ No newline at end of file diff --git a/airlock_libs/src/prelude.rs b/airlock_libs/src/prelude.rs new file mode 100644 index 0000000..faa125a --- /dev/null +++ b/airlock_libs/src/prelude.rs @@ -0,0 +1,27 @@ +pub use chrono::{Duration, Local, NaiveDate}; +pub use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle}; +pub use mongodb::bson::oid::ObjectId; +pub use opentelemetry::global::shutdown_tracer_provider; +pub use opentelemetry::sdk::Resource; +pub use opentelemetry::trace::noop::NoopTracerProvider; +pub use opentelemetry::trace::{Status, TraceContextExt, TraceError}; +pub use opentelemetry::{Context, KeyValue, sdk::trace as sdktrace, trace::Tracer}; +pub use opentelemetry::{Key, global}; +pub use opentelemetry_otlp::WithExportConfig; +pub use pyo3::{prelude::*, types::PyString}; +pub use pyo3_async_runtimes::async_std; +pub use reqwest::{ + Client, + header::{HeaderMap, HeaderName, HeaderValue}, +}; +pub use serde::{Deserialize, Serialize}; +pub use serde_json::Value; +pub use std::{ + collections::HashMap, + env, + fmt::Write, + fs::{self, File}, + io::{Read, Seek, SeekFrom}, + path::PathBuf, + str::FromStr, +}; \ No newline at end of file diff --git a/airlock_libs/src/services.rs b/airlock_libs/src/services.rs index 14f191d..e38c7e0 100644 --- a/airlock_libs/src/services.rs +++ b/airlock_libs/src/services.rs @@ -1,128 +1,5 @@ -use chrono::{Duration, Local, NaiveDate}; -use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle}; -use mongodb::bson::oid::ObjectId; -use opentelemetry::global::shutdown_tracer_provider; -use opentelemetry::sdk::Resource; -use opentelemetry::trace::noop::NoopTracerProvider; -use opentelemetry::trace::{Status, TraceContextExt, TraceError}; -use opentelemetry::{Context, KeyValue, sdk::trace as sdktrace, trace::Tracer}; -use opentelemetry::{Key, global}; -use opentelemetry_otlp::WithExportConfig; -use pyo3::{prelude::*, types::PyString}; -use pyo3_async_runtimes::async_std; -use reqwest::{ - Client, - header::{HeaderMap, HeaderName, HeaderValue}, -}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use std::{ - collections::HashMap, - env, - fmt::Write, - fs::{self, File}, - io::{Read, Seek, SeekFrom}, - path::PathBuf, - str::FromStr, -}; - -#[allow(non_snake_case)] -#[derive(Deserialize, Debug)] -struct TelemetryConfig { - TELEMETRY: bool, - TELEM_URL: Option, -} - -impl TelemetryConfig { - pub fn load() -> Self { - let cfg_path = get_base_directory().join("config\\user_config.json"); - if !cfg_path.exists() { - return Self { - TELEMETRY: false, - TELEM_URL: None, - }; - } - match fs::read_to_string(&cfg_path) { - Ok(contents) => serde_json::from_str::(&contents).unwrap_or(Self { - TELEMETRY: false, - TELEM_URL: None, - }), - Err(_) => Self { - TELEMETRY: false, - TELEM_URL: None, - }, - } - } -} -#[derive(Debug, Deserialize, Serialize)] -struct ApiResponse { - error: String, - response: ExecHistories, -} -#[derive(Debug, Deserialize, Serialize)] -struct ExecHistories { - exechistories: Vec, -} -#[derive(Debug, Deserialize, Serialize, Clone)] -struct Group { - checkpoint: String, - #[serde(rename = "type")] - exectype: u8, - username: String, - hostname: String, - netdomain: String, - filename: String, - ppolicy: String, - policyname: String, - policyver: String, - commandline: String, - publisher: String, - pprocess: String, - gprocess: String, - sha256: String, - datetime: String, - md5: String, - sha128: String, - sha384: String, - sha512: String, - ip: String, - localip: String, -} - -enum ExtractedValues { - Headers(reqwest::header::HeaderMap), - BaseUrl(String), -} - -trait Converter { - fn convert(py: Python<'_>, py_self: &Py, extract_headers: bool) -> ExtractedValues; -} - -struct PyData; - -impl Converter for PyData { - fn convert(py: Python<'_>, py_self: &Py, extract_headers: bool) -> ExtractedValues { - if extract_headers { - let headers = py_self.getattr(py, "headers").unwrap().to_string(); - let headers_replace = headers.replace('\'', "\""); - let parsed: Value = serde_json::from_str(headers_replace.as_str()).unwrap(); - let mut header_map = HeaderMap::new(); - if let Some(obj) = parsed.as_object() { - for (_key, value) in obj { - if let Some(v) = value.as_str() { - let val = HeaderValue::from_str(v).unwrap(); - header_map.insert(HeaderName::from_str("X-APIKey").unwrap(), val); - } - } - } - ExtractedValues::Headers(header_map) - } else { - let base_url = py_self.getattr(py, "base_url").unwrap().to_string(); - ExtractedValues::BaseUrl(base_url) - } - } -} - +use crate::prelude::*; +use crate::modules::datatypes::*; #[pyfunction] pub fn pull_policy_exec_histories( py: Python<'_>, @@ -192,7 +69,7 @@ pub fn pull_policy_exec_histories( std::process::abort(); } } - let mut checkpoint_number: String = skipback(days).to_string(); + let mut checkpoint_number: String = SkipBack::find_checkpoint(days).to_string(); let multi_progress = MultiProgress::new(); multi_progress.set_draw_target(ProgressDrawTarget::stdout()); let progress_bar = multi_progress.add(ProgressBar::new(100)); @@ -410,7 +287,7 @@ async fn history_logging( } } -fn get_base_directory() -> PathBuf { +pub fn get_base_directory() -> PathBuf { let home = env::var_os("HOME") .map(PathBuf::from) .or_else(|| env::var_os("USERPROFILE").map(PathBuf::from)) @@ -427,15 +304,6 @@ fn get_base_directory() -> PathBuf { } } -fn skipback(days: i64) -> ObjectId { - let date_days_ago = Local::now() - Duration::days(days); - let timestamp = date_days_ago.timestamp() as u32; - let mut hex_timestamp = String::new(); - write!(&mut hex_timestamp, "{:08x}", timestamp).unwrap(); - let objectid_hex = format!("{}0000000000000000", hex_timestamp); - ObjectId::parse_str(&objectid_hex).expect("Invalid ObjectId hex") -} - fn init_tracer() -> Result, TraceError> { let cfg = TelemetryConfig::load(); if !cfg.TELEMETRY { @@ -457,4 +325,4 @@ fn init_tracer() -> Result, TraceError> { .install_simple() .unwrap(); Ok(Some(tracer)) -} +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 336bdd8..7c0d5ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,4 @@ urllib3==2.5.0 pyperclip==1.11.0 --extra-index-url https://git.racooncity.org/api/packages/brotoskyj/pypi/simple/ -airlock_libs==5.0.0 \ No newline at end of file +airlock_libs==5.0.1 \ No newline at end of file