From 0d037bf18de40be7ece7006c1df5226bc46058dc Mon Sep 17 00:00:00 2001 From: Sebastien Rousseau Date: Sun, 10 Sep 2023 10:15:16 +0100 Subject: [PATCH] feat(hsh): decoupling models --- Cargo.lock | 4 +- Cargo.toml | 2 +- benches/criterion.rs | 2 +- examples/hsh.rs | 3 +- src/constants.rs | 0 src/lib.rs | 55 ++++++----- src/loggers.rs | 228 +++++++++++++++++++++++++++++++++++++++++++ src/macros.rs | 6 +- src/models/data.rs | 30 ++++++ src/models/mod.rs | 5 + tests/test_lib.rs | 1 + tests/test_macros.rs | 3 +- 12 files changed, 304 insertions(+), 35 deletions(-) create mode 100644 src/constants.rs create mode 100644 src/loggers.rs create mode 100644 src/models/data.rs create mode 100644 src/models/mod.rs diff --git a/Cargo.lock b/Cargo.lock index d8adda7..5070395 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.21.3" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" diff --git a/Cargo.toml b/Cargo.toml index eddbfc1..8183ff4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ path = "examples/hsh.rs" [dependencies] argon2rs = "0.2.5" -base64 = "0.21.3" +base64 = "0.21.4" bcrypt = "0.15.0" scrypt = "0.11.0" serde = { version = "1.0.188", features = ["derive"] } diff --git a/benches/criterion.rs b/benches/criterion.rs index ce8f822..530a4d1 100644 --- a/benches/criterion.rs +++ b/benches/criterion.rs @@ -10,7 +10,7 @@ use criterion::{ }; extern crate hsh; -use self::hsh::Hash; +use hsh::models::data::Hash; fn generate_hash_benchmark(c: &mut Criterion) { c.bench_function("generate_hash", |b| { diff --git a/examples/hsh.rs b/examples/hsh.rs index fa241f4..388ae4d 100644 --- a/examples/hsh.rs +++ b/examples/hsh.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Using the Hash (HSH) library -use hsh::{new_hash, Hash, HashAlgorithm}; +use hsh::{new_hash, HashAlgorithm}; +use hsh::models::data::*; use std::str::FromStr; // Creating and verifying hashes diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs index 147e5b0..d4747d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,7 @@ //! ```rust //! // Import the Hash struct //! extern crate hsh; -//! use hsh::Hash; +//! use hsh::models::data::Hash; //! //! // Main function //! fn main() { @@ -169,38 +169,41 @@ extern crate base64; extern crate bcrypt; extern crate scrypt; extern crate vrd; +use crate::models::data::*; use argon2rs::argon2i_simple; use base64::{engine::general_purpose, Engine as _}; - use scrypt::scrypt; use serde::{Deserialize, Serialize}; use std::{fmt, str::FromStr}; use vrd::Random; -/// A type alias for a salt. -pub type Salt = Vec; - -/// A struct for storing and verifying hashed passwords based on the argon2rs crate -#[non_exhaustive] -#[derive( - Clone, - Debug, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - Deserialize, -)] -pub struct Hash { - /// The password hash. - pub hash: Vec, - /// The salt used for hashing - pub salt: Salt, - /// The hash algorithm used - pub algorithm: HashAlgorithm, -} +/// The `models` module contains the data models for the library. +pub mod models; + +// /// A type alias for a salt. +// pub type Salt = Vec; + +// /// A struct for storing and verifying hashed passwords based on the argon2rs crate +// #[non_exhaustive] +// #[derive( +// Clone, +// Debug, +// Eq, +// Hash, +// Ord, +// PartialEq, +// PartialOrd, +// Serialize, +// Deserialize, +// )] +// pub struct Hash { +// /// The password hash. +// pub hash: Vec, +// /// The salt used for hashing +// pub salt: Salt, +// /// The hash algorithm used +// pub algorithm: HashAlgorithm, +// } /// The supported hash algorithms #[non_exhaustive] diff --git a/src/loggers.rs b/src/loggers.rs new file mode 100644 index 0000000..7f62b33 --- /dev/null +++ b/src/loggers.rs @@ -0,0 +1,228 @@ +// Copyright © 2023 Hash (HSH) library. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Standard library imports for formatting and I/O operations. +use std::{ + fmt, + io::{self, Write as IoWrite}, + fs::OpenOptions, +}; + + +/// Enum representing the different log formats that can be used. +/// +/// This enum allows the developer to specify the format in which log messages should be displayed. +/// +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub enum LogFormat { + /// The log format is set to the Common Log Format (CLF) + CLF, + /// The log format is set to the JSON format + JSON, + /// The log format is set to the Common Event Format (CEF) + CEF, + /// The log format is set to the Extended Log Format (ELF) + ELF, + /// The log format is set to the W3C Extended Log File Format + W3C, + /// The log format is set to the Graylog Extended Log Format (GELF) + GELF, +} + +/// Implements Display trait for LogFormat enum. +/// +/// This allows easy conversion of the log format enums to strings. +impl fmt::Display for LogFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{:?}", self) + } +} + +/// An enumeration of the different levels that a log message can have. +/// Each variant of the enumeration represents a different level of +/// importance. +/// +/// # Arguments +/// +/// * `ALL` - The log level is set to all. +/// * `DEBUG` - The log level is set to debug. +/// * `DISABLED` - The log level is set to disabled. +/// * `ERROR` - The log level is set to error. +/// * `FATAL` - The log level is set to fatal. +/// * `INFO` - The log level is set to info. +/// * `NONE` - The log level is set to none. +/// * `TRACE` - The log level is set to trace. +/// * `VERBOSE` - The log level is set to verbose. +/// * `WARNING` - The log level is set to warning. +/// +#[derive(Debug, PartialEq, Eq, Hash, Clone,PartialOrd)] +pub enum LogLevel { + /// The log level is set to all. + ALL, + /// The log level is set to debug. + DEBUG, + /// The log level is set to disabled. + DISABLED, + /// The log level is set to error. + ERROR, + /// The log level is set to fatal. + FATAL, + /// The log level is set to info. + INFO, + /// The log level is set to none. + NONE, + /// The log level is set to trace. + TRACE, + /// The log level is set to verbose. + VERBOSE, + /// The log level is set to warning. + WARNING, +} +/// Display trait implementation for `LogLevel`. +/// +/// This converts the enum to a string representation. +impl fmt::Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Struct representing a log message. +/// +/// Contains all the elements that make up a complete log message. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Log { + /// A string that holds a session ID. The session ID is a unique + /// identifier for the current session. A random GUID (Globally + /// Unique Identifier) is generated by default. + pub session_id: String, + /// A string that holds the timestamp in ISO 8601 format. + pub time: String, + /// A string that holds the level (INFO, WARN, ERROR, etc.). + pub level: LogLevel, + /// A string that holds the component name. + pub component: String, + /// A string that holds the description of the log message. + pub description: String, + /// A string that holds the log format. + pub format: LogFormat, +} + +impl Log { + /// Logs a message to the console using a pre-allocated buffer to + /// reduce memory allocation and flush the output buffer to ensure + /// that the message is written immediately. + /// + /// # Errors + /// + /// This function will panic if an error occurs when writing to the + /// pre-allocated buffer or flushing the output buffer. + pub fn log(&self) -> io::Result<()> { + // Open the file in append mode. If the file does not exist, create it. + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .open("shokunin.log")?; + match self.format { + LogFormat::CLF => { + writeln!( + file, + "SessionID={}\tTimestamp={}\tDescription={}\tLevel={}\tComponent={}\tFormat={}", + self.session_id, self.time, self.description, self.level, self.component, self.format + ) + }, + LogFormat::JSON => { + writeln!( + file, + r#"{{"session_id": "{}", "timestamp": "{}", "description": "{}", "level": "{}", "component": "{}", "format": "{}"}}"#, + self.session_id, self.time, self.description, self.level, self.component, self.format + ) + }, + LogFormat::CEF => { + writeln!( + file, + r#"[CEF] + + 1 + shokunin + Application + {} + Log + {} + {} + {} + localhost + localhost + - + - + - + - + - + - + + "#, + self.time, self.level, self.description, self.session_id + ) + }, + _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Unsupported log format")), + }?; + file.flush()?; + Ok(()) + } + + /// Creates a new `Log` instance. + /// + /// Initializes a new `Log` struct with the provided details. + /// + /// # Returns + /// + /// Returns a new instance of the `Log` struct. + pub fn new( + session_id: &str, + time: &str, + level: LogLevel, + component: &str, + description: &str, + format: LogFormat, + ) -> Self { + Self { + session_id: session_id.to_string(), + time: time.to_string(), + level, + component: component.to_string(), + description: description.to_string(), + format, + } + } +} + +/// Provides default values for `Log`. +/// +/// This implementation provides a quick way to generate a `Log` instance with default values. +impl Default for Log { + fn default() -> Self { + Self { + session_id: String::default(), + time: String::default(), + level: LogLevel::INFO, // Default log level + component: String::default(), + description: String::default(), + format: LogFormat::CLF, // Default log format + } + } +} +#[cfg(test)] +/// Tests for the `log_info!` macro. +mod tests { + use crate::macro_log_info; + + #[test] + fn test_log_info() { + macro_log_info!( + LogLevel::INFO, + "component", + "description", + LogFormat::CLF + ); + } +} \ No newline at end of file diff --git a/src/macros.rs b/src/macros.rs index 44926e5..a63a748 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -366,7 +366,7 @@ macro_rules! match_algo { /// /// ``` /// extern crate hsh; -/// use hsh::Hash; +/// use hsh::models::data::Hash; /// use hsh::{ generate_hash, HashAlgorithm }; /// /// let password = "password"; @@ -391,7 +391,7 @@ macro_rules! generate_hash { /// /// ``` /// extern crate hsh; -/// use hsh::Hash; +/// use hsh::models::data::Hash; /// use hsh::{ new_hash, HashAlgorithm }; /// /// let password = "password"; @@ -415,7 +415,7 @@ macro_rules! new_hash { /// /// ``` /// extern crate hsh; -/// use hsh::Hash; +/// use hsh::models::data::Hash; /// use hsh::{ hash_length }; /// use hsh::{ new_hash, HashAlgorithm }; /// diff --git a/src/models/data.rs b/src/models/data.rs new file mode 100644 index 0000000..d8adac5 --- /dev/null +++ b/src/models/data.rs @@ -0,0 +1,30 @@ +// Copyright © 2023 Hash (HSH) library. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use crate::HashAlgorithm; +use serde::{Deserialize, Serialize}; + +/// A type alias for a salt. +pub type Salt = Vec; + +/// A struct for storing and verifying hashed passwords based on the argon2rs crate +#[non_exhaustive] +#[derive( + Clone, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize, + Deserialize, +)] +pub struct Hash { + /// The password hash. + pub hash: Vec, + /// The salt used for hashing + pub salt: Salt, + /// The hash algorithm used + pub algorithm: HashAlgorithm, +} \ No newline at end of file diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..66d37e3 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,5 @@ +// Copyright © 2023 Hash (HSH) library. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// The `data` module contains the structs. +pub mod data; \ No newline at end of file diff --git a/tests/test_lib.rs b/tests/test_lib.rs index caca6ea..2014b90 100644 --- a/tests/test_lib.rs +++ b/tests/test_lib.rs @@ -4,6 +4,7 @@ #[cfg(test)] mod tests { use hsh::*; + use hsh::models::data::Hash; #[test] fn test_new() { diff --git a/tests/test_macros.rs b/tests/test_macros.rs index 7defe39..efb902b 100644 --- a/tests/test_macros.rs +++ b/tests/test_macros.rs @@ -6,7 +6,8 @@ mod tests { // Importing hsh crate and all of its macros extern crate hsh; - use hsh::{generate_hash, hash_length, new_hash, Hash}; + use hsh::{generate_hash, hash_length, new_hash}; + use hsh::models::data::*; use hsh::{ hsh_assert, hsh_contains, hsh_in_range, hsh_join, hsh_max, hsh_min, hsh_parse, hsh_print, hsh_print_vec, hsh_split,