diff --git a/Cargo.lock b/Cargo.lock index 89239d7..4ef32e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", @@ -169,9 +169,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.104" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" [[package]] name = "cfg-if" @@ -246,9 +246,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -256,9 +256,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", @@ -674,9 +674,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -1277,11 +1277,12 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "request-mirror" -version = "0.1.1" +version = "0.1.2" dependencies = [ "chrono", "diesel", "dotenvy", + "regex", "rocket", "rocket_dyn_templates", "serde", @@ -1497,18 +1498,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -1621,9 +1622,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.68" +version = "2.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" dependencies = [ "proc-macro2", "quote", @@ -1790,9 +1791,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap", "serde", @@ -1925,9 +1926,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "uuid" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", "rand", @@ -1936,9 +1937,9 @@ dependencies = [ [[package]] name = "uuid-macro-internal" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ff64d5cde1e2cb5268bdb497235b6bd255ba8244f910dbc3574e59593de68c" +checksum = "ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml old mode 100755 new mode 100644 index 3e48eb2..c2886dd --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "request-mirror" -version = "0.1.1" +version = "0.1.2" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,6 +9,7 @@ edition = "2021" chrono = "0.4.34" diesel = { version = "2.1.4", features = ["postgres", "chrono"] } dotenvy = "0.15.7" +regex = "1.10.5" rocket = { version = "0.5.1", features = ["tls"] } serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.114" diff --git a/src/lib.rs b/src/lib.rs index 19b6811..e835b44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,10 +6,18 @@ use diesel::prelude::*; use std::env; use dotenvy::dotenv; use models::{ - Client, NewClient, NewHistoryRecord, NewPairRecord, PairType + Client, + NewClient, + NewHistoryRecord, + NewPairRecord, + PairType, + Ownership }; use schema::{ - clients, history, pair_records + clients, + history, + pair_records, + ownership }; /// Establishes diesel Postgres connection that can be used to iteract with the database @@ -88,3 +96,36 @@ pub fn create_pair_record(conn: &mut PgConnection, history_id: i64, pair_type: P .values(&new_pair_record) .execute(conn).unwrap() } + +/// Creates a new ownership record in the database +/// Pass the owner_id and client_id +pub fn create_owner_record(conn: &mut PgConnection, owner_id: String, client_id: String) -> usize { + let new_ownership_record = Ownership { + owner_id, + client_id + }; + + diesel::insert_into(ownership::table) + .values(&new_ownership_record) + .execute(conn).unwrap() +} + +/// retrieve a Vec of ownership relationships which client_id owns +pub fn get_ownerships(client_id: &str, connection: &mut PgConnection) -> Vec { + ownership::dsl::ownership + .filter(ownership::owner_id.eq(client_id)) + .select(Ownership::as_select()) + .load(connection) + .expect("Error loading history records") +} + +/// Returns boolean for whether the given ownership exists between client_id and owner_id +pub fn ownership_exists(client_id: &str, owner_id: &str, connection: &mut PgConnection) -> bool { + let ownerships = ownership::dsl::ownership + .filter(ownership::client_id.eq(client_id).and(ownership::owner_id.eq(owner_id))) + .select(Ownership::as_select()) + .load(connection) + .expect("Error loading history records"); + + ownerships.len() > 0 +} diff --git a/src/main.rs b/src/main.rs index a65909b..69a19b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,22 +13,33 @@ use uuid::Uuid; use request_mirror::models::*; use diesel::prelude::*; use request_mirror::*; +use regex::Regex; +/// Contains information about a request #[derive(Serialize, Debug, Clone)] -struct RequestInfo { +pub struct RequestInfo { header: Vec, cookies: Vec, query: Vec } -#[derive(Debug)] -enum Error { - TooLarge, - Io(std::io::Error), +impl RequestInfo { + /// Find query pair with given key + pub fn find_query_key(&self, key: &str) -> Option<&Pair> { + + for pair in self.query.iter() { + if pair.key == key { + return Some(pair); + } + } + + None + } } +/// Contains string representation of body of a request #[derive(Serialize, Debug, Clone)] -struct RequestBody(String); +pub struct RequestBody(String); // Always use a limit to prevent DoS attacks. const LIMIT: u64 = 256; @@ -257,12 +268,28 @@ fn history_req(cookies: &CookieJar<'_>) -> Template { let connection = &mut establish_connection(); // Query the database for history records - let results = history::dsl::history + let mut results = history::dsl::history .filter(history::client_id.eq(client_id.to_string())) .select(HistoryRecord::as_select()) .load(connection) .expect("Error loading clients"); + // Get ownership relationships + let ownerships: Vec = get_ownerships(client_id, connection); + + // Add any records that owned clients have + for ownership in ownerships { + let mut owned_records: Vec = history::dsl::history + .filter(history::client_id.eq(ownership.client_id.to_string())) + .select(HistoryRecord::as_select()) + .load(connection) + .expect("Error loading history records"); + + results.append(&mut owned_records); + } + + results.sort_unstable_by_key(|item| item.id); + /// Template Context #[derive(Serialize)] struct Context { @@ -302,13 +329,19 @@ fn history_details(history_id: i64, cookies: &CookieJar<'_>) -> Template { let client_id = cookies.get("mirror-id").unwrap().value(); - let connection = &mut establish_connection(); + let connection: &mut PgConnection = &mut establish_connection(); + + // Get owned client ids + let owned_clients: Vec = get_ownerships(client_id, connection) + .iter() + .map(|x|x.client_id.to_string()) + .collect::>(); // Query history table where the history id is what was in the route. Also filter client_ids. // If this record has a different client_id nothing will be returned let results: Vec = history::dsl::history .filter(history::id.eq(&history_id)) - .filter(history::client_id.eq(client_id.to_string())) + .filter(history::client_id.eq(client_id.to_string()).or(history::client_id.eq_any(owned_clients))) .select(HistoryRecord::as_select()) .load(connection) .expect("Error loading history records"); @@ -322,8 +355,6 @@ fn history_details(history_id: i64, cookies: &CookieJar<'_>) -> Template { ); } - let connection = &mut establish_connection(); - // Query all Pair Records for this history_id let pairs: Vec = pair_records::dsl::pair_records .filter(pair_records::history_id.eq(history_id)) @@ -381,6 +412,48 @@ fn history_details(history_id: i64, cookies: &CookieJar<'_>) -> Template { } +#[get("/ownership_registration")] +fn ownership_registration(info: RequestInfo, cookies: &CookieJar<'_>) -> Template { + + let client_id = cookies.get("mirror-id").unwrap().value().to_string(); + let owner_id_pair = info.find_query_key("owner_id"); + let mut disp_owner_reg = false; + let re = Regex::new(r"^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$").unwrap(); + let connection: &mut PgConnection = &mut establish_connection(); + let ownerships: Vec = get_ownerships(&client_id, connection); + + let owner_id = match owner_id_pair { + Some(v) => v.value.to_string(), + None => "".to_string() + }; + + if + owner_id_pair.is_some() && + re.is_match(&owner_id) && + owner_id != client_id && + !ownership_exists(&client_id, &owner_id, connection) + { + create_owner_record(connection, owner_id.clone(), client_id.clone()); + disp_owner_reg = true; + } + + #[derive(Serialize)] + struct Context{ + client_id: String, + owner_id: String, + disp_owner_reg: bool, + ownerships: Vec + } + + Template::render("ownership_registration", Context { + client_id, + owner_id, + disp_owner_reg, + ownerships + }) +} + + /// 404 Response #[catch(404)] fn not_found(req: &Request) -> Template { @@ -402,7 +475,8 @@ fn rocket() -> _ { test_get, test_post, history_req, - history_details + history_details, + ownership_registration ] ) .register("/", catchers![not_found]) diff --git a/src/models.rs b/src/models.rs index b21ac78..95b9219 100644 --- a/src/models.rs +++ b/src/models.rs @@ -4,6 +4,7 @@ use serde::Serialize; use crate::schema::{ clients, history, + ownership, pair_records }; use diesel::pg::Pg; @@ -58,6 +59,16 @@ pub struct NewHistoryRecord<'a> { pub timestamp: NaiveDateTime } +/// Used to store ownership relationship between two clients +/// This structure is used for allowing separate clients to view history records of other clients +#[derive(Queryable, Selectable, Insertable, Debug, Serialize)] +#[diesel(table_name = ownership)] +#[diesel(check_for_backend(Pg))] +pub struct Ownership { + pub owner_id: String, + pub client_id: String +} + /// Pair Records are used to store attributes of a request. This includes a body record, header, query and cookie record. /// Each has a key and value. #[derive(Queryable, Selectable, Debug, Serialize)] @@ -104,4 +115,11 @@ pub struct Pair { #[derive(Serialize)] pub struct ErrorContext { pub error_msg: String +} + +/// Provides error context for +#[derive(Debug)] +pub enum Error { + TooLarge, + Io(std::io::Error), } \ No newline at end of file diff --git a/templates/error.html.hbs b/templates/error.html.hbs index 95e6e4b..5ebb631 100644 --- a/templates/error.html.hbs +++ b/templates/error.html.hbs @@ -8,18 +8,11 @@ -