From f6929c2575d33ac85c515602e7f371c93b7d47ef Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 20 Oct 2023 17:25:19 +0200 Subject: [PATCH 01/29] Initial Rust test --- Cargo.lock | 99 +++++++++++++++++++++++++++++++++++ Cargo.toml | 11 ++++ src/main.rs | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5fd5897 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,99 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +dependencies = [ + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "nfsense" +version = "0.1.0" +dependencies = [ + "ipnet", + "serde", + "serde_json", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "serde" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4c7074f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "nfsense" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ipnet = { version = "2.8.0", features = ["serde"] } +serde = { version = "1.0.189", features = ["derive"] } +serde_json = "1.0.107" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..452de31 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,145 @@ +#![allow(dead_code)] + +use ipnet::IpNet; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, net::IpAddr}; + +#[derive(Serialize, Deserialize, Default, Debug)] +struct Config { + config_version: u64, + network: Network, +} + +#[derive(Serialize, Deserialize, Default, Debug)] +struct Network { + interfaces: HashMap, + static_routes: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct NetworkInterface { + alias: String, + comment: String, + interface_type: NetworkInterfaceType, + addressing_mode: AddressingMode, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +enum NetworkInterfaceType { + Hardware { device: String }, + Vlan { id: i32, parent: String }, + Bond { members: Vec }, + Bridge { members: Vec }, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +enum AddressingMode { + None, + Static { address: String }, + DHCP, +} + +#[derive(Serialize, Deserialize, Debug)] +struct StaticRoute { + name: String, + interface: String, + gateway: IpAddr, + destination: IpNet, + metric: u64, +} + +#[derive(Serialize, Deserialize, Default, Debug)] +struct Object { + addresses: HashMap, + services: HashMap, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Address { + address_type: AddressType, + comment: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +enum AddressType { + Host { host: String }, + Range { range: IpAddr }, + Network { network: IpNet }, + Group { children: Vec }, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Service { + service_type: ServiceType, + comment: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +enum ServiceType { + TCP { + source_port_start: u64, + source_port_end: u64, + destination_port_start: u64, + destination_port_end: u64, + }, + UDP { + range: IpAddr, + }, + ICMP { + code: u8, + }, + Group { + children: Vec, + }, +} + +fn main() { + println!("Hello, world!"); + + let mut config = Config::default(); + config.config_version = 1; + + config.network.interfaces.insert( + "inter1".to_string(), + NetworkInterface { + alias: "test".to_owned(), + comment: "test comment".to_owned(), + interface_type: NetworkInterfaceType::Hardware { + device: "eth0".to_owned(), + }, + addressing_mode: AddressingMode::None, + }, + ); + + config.network.interfaces.insert( + "inter2".to_string(), + NetworkInterface { + alias: "test2".to_owned(), + comment: "test comment".to_owned(), + interface_type: NetworkInterfaceType::Hardware { + device: "eth0".to_owned(), + }, + addressing_mode: AddressingMode::Static { + address: "192.168.1.1".to_owned(), + }, + }, + ); + + config.network.static_routes.push(StaticRoute { + name: "test1".to_string(), + interface: "eth0".to_string(), + gateway: "192.168.1.1".parse().unwrap(), + destination: "10.42.42.0/24".parse().unwrap(), + metric: 0, + }); + + let serialized = serde_json::to_string_pretty(&config).unwrap(); + println!("serialized = {}", serialized); + + let deserialized: Config = serde_json::from_str(&serialized).unwrap(); + println!("deserialized = {:?}", deserialized); +} From 93c15c086ffd73b1cc86731afb67fe6449ed9f6b Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 20 Oct 2023 17:26:32 +0200 Subject: [PATCH 02/29] Add gitignore and vscode settings --- .gitignore | 1 + .vscode/settings.json | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0196fd7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true + } +} \ No newline at end of file From 4fdfb151de2d122193fb42265a8702debdcfb65d Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 20 Oct 2023 18:13:05 +0200 Subject: [PATCH 03/29] add validator, fix service port --- Cargo.lock | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/main.rs | 28 ++++--- 3 files changed, 241 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fd5897..072cd4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,51 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "ipnet" version = "2.8.0" @@ -17,6 +62,24 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + [[package]] name = "nfsense" version = "0.1.0" @@ -24,6 +87,37 @@ dependencies = [ "ipnet", "serde", "serde_json", + "validator", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] @@ -44,6 +138,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "ryu" version = "1.0.15" @@ -67,7 +190,7 @@ checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -81,6 +204,17 @@ dependencies = [ "serde", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -92,8 +226,97 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna 0.4.0", + "percent-encoding", +] + +[[package]] +name = "validator" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f07b0a1390e01c0fc35ebb26b28ced33c9a3808f7f9fbe94d3cc01e233bfeed5" +dependencies = [ + "idna 0.2.3", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea7ed5e8cf2b6bdd64a6c4ce851da25388a89327b17b88424ceced6bd5017923" +dependencies = [ + "if_chain", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ddf34293296847abfc1493b15c6e2f5d3cd19f57ad7d22673bf4c6278da329" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/Cargo.toml b/Cargo.toml index 4c7074f..94e8aa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" ipnet = { version = "2.8.0", features = ["serde"] } serde = { version = "1.0.189", features = ["derive"] } serde_json = "1.0.107" +validator = { version = "0.15", features = ["derive"] } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 452de31..b6c1939 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,20 +3,21 @@ use ipnet::IpNet; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, net::IpAddr}; +use validator::Validate; -#[derive(Serialize, Deserialize, Default, Debug)] +#[derive(Serialize, Deserialize, Validate, Default, Debug)] struct Config { config_version: u64, network: Network, } -#[derive(Serialize, Deserialize, Default, Debug)] +#[derive(Serialize, Deserialize, Validate, Default, Debug)] struct Network { interfaces: HashMap, static_routes: Vec, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Validate, Debug)] struct NetworkInterface { alias: String, comment: String, @@ -41,7 +42,7 @@ enum AddressingMode { DHCP, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Validate, Debug)] struct StaticRoute { name: String, interface: String, @@ -50,13 +51,13 @@ struct StaticRoute { metric: u64, } -#[derive(Serialize, Deserialize, Default, Debug)] +#[derive(Serialize, Deserialize, Validate, Default, Debug)] struct Object { addresses: HashMap, services: HashMap, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Validate, Debug)] struct Address { address_type: AddressType, comment: String, @@ -71,7 +72,7 @@ enum AddressType { Group { children: Vec }, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Validate, Debug)] struct Service { service_type: ServiceType, comment: String, @@ -81,13 +82,16 @@ struct Service { #[serde(rename_all = "snake_case")] enum ServiceType { TCP { - source_port_start: u64, - source_port_end: u64, - destination_port_start: u64, - destination_port_end: u64, + source_port: u64, + source_port_end: Option, + destination_port: u64, + destination_port_end: Option, }, UDP { - range: IpAddr, + source_port: u64, + source_port_end: Option, + destination_port: u64, + destination_port_end: Option, }, ICMP { code: u8, From e67c7679a205dbb2a0e049a407a11f3d72f60f6a Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 20 Oct 2023 23:35:05 +0200 Subject: [PATCH 04/29] move definitions to seperate files --- src/definitions.rs | 12 ++++ src/definitions/network.rs | 44 +++++++++++++ src/definitions/object.rs | 54 +++++++++++++++ src/main.rs | 130 +++++-------------------------------- 4 files changed, 128 insertions(+), 112 deletions(-) create mode 100644 src/definitions.rs create mode 100644 src/definitions/network.rs create mode 100644 src/definitions/object.rs diff --git a/src/definitions.rs b/src/definitions.rs new file mode 100644 index 0000000..a1dc9c8 --- /dev/null +++ b/src/definitions.rs @@ -0,0 +1,12 @@ +pub mod network; +pub mod object; + +use serde::{Deserialize, Serialize}; +use validator::Validate; + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct Config { + pub config_version: u64, + pub network: network::Network, + pub object: object::Object, +} diff --git a/src/definitions/network.rs b/src/definitions/network.rs new file mode 100644 index 0000000..0a8aa53 --- /dev/null +++ b/src/definitions/network.rs @@ -0,0 +1,44 @@ +use ipnet::IpNet; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, net::IpAddr}; +use validator::Validate; + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct Network { + pub interfaces: HashMap, + pub static_routes: Vec, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct NetworkInterface { + pub alias: String, + pub comment: String, + pub interface_type: NetworkInterfaceType, + pub addressing_mode: AddressingMode, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum NetworkInterfaceType { + Hardware { device: String }, + Vlan { id: i32, parent: String }, + Bond { members: Vec }, + Bridge { members: Vec }, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum AddressingMode { + None, + Static { address: String }, + DHCP, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct StaticRoute { + pub name: String, + pub interface: String, + pub gateway: IpAddr, + pub destination: IpNet, + pub metric: u64, +} diff --git a/src/definitions/object.rs b/src/definitions/object.rs new file mode 100644 index 0000000..123c581 --- /dev/null +++ b/src/definitions/object.rs @@ -0,0 +1,54 @@ +use ipnet::IpNet; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, net::IpAddr}; +use validator::Validate; + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct Object { + pub addresses: HashMap, + pub services: HashMap, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct Address { + pub address_type: AddressType, + pub comment: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum AddressType { + Host { host: String }, + Range { range: IpAddr }, + Network { network: IpNet }, + Group { children: Vec }, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct Service { + pub service_type: ServiceType, + pub comment: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum ServiceType { + TCP { + source_port: u64, + source_port_end: Option, + destination_port: u64, + destination_port_end: Option, + }, + UDP { + source_port: u64, + source_port_end: Option, + destination_port: u64, + destination_port_end: Option, + }, + ICMP { + code: u8, + }, + Group { + children: Vec, + }, +} diff --git a/src/main.rs b/src/main.rs index b6c1939..0a85a13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,105 +1,8 @@ #![allow(dead_code)] -use ipnet::IpNet; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, net::IpAddr}; -use validator::Validate; +mod definitions; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] -struct Config { - config_version: u64, - network: Network, -} - -#[derive(Serialize, Deserialize, Validate, Default, Debug)] -struct Network { - interfaces: HashMap, - static_routes: Vec, -} - -#[derive(Serialize, Deserialize, Validate, Debug)] -struct NetworkInterface { - alias: String, - comment: String, - interface_type: NetworkInterfaceType, - addressing_mode: AddressingMode, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -enum NetworkInterfaceType { - Hardware { device: String }, - Vlan { id: i32, parent: String }, - Bond { members: Vec }, - Bridge { members: Vec }, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -enum AddressingMode { - None, - Static { address: String }, - DHCP, -} - -#[derive(Serialize, Deserialize, Validate, Debug)] -struct StaticRoute { - name: String, - interface: String, - gateway: IpAddr, - destination: IpNet, - metric: u64, -} - -#[derive(Serialize, Deserialize, Validate, Default, Debug)] -struct Object { - addresses: HashMap, - services: HashMap, -} - -#[derive(Serialize, Deserialize, Validate, Debug)] -struct Address { - address_type: AddressType, - comment: String, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -enum AddressType { - Host { host: String }, - Range { range: IpAddr }, - Network { network: IpNet }, - Group { children: Vec }, -} - -#[derive(Serialize, Deserialize, Validate, Debug)] -struct Service { - service_type: ServiceType, - comment: String, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "snake_case")] -enum ServiceType { - TCP { - source_port: u64, - source_port_end: Option, - destination_port: u64, - destination_port_end: Option, - }, - UDP { - source_port: u64, - source_port_end: Option, - destination_port: u64, - destination_port_end: Option, - }, - ICMP { - code: u8, - }, - Group { - children: Vec, - }, -} +use definitions::Config; fn main() { println!("Hello, world!"); @@ -109,37 +12,40 @@ fn main() { config.network.interfaces.insert( "inter1".to_string(), - NetworkInterface { + definitions::network::NetworkInterface { alias: "test".to_owned(), comment: "test comment".to_owned(), - interface_type: NetworkInterfaceType::Hardware { + interface_type: definitions::network::NetworkInterfaceType::Hardware { device: "eth0".to_owned(), }, - addressing_mode: AddressingMode::None, + addressing_mode: definitions::network::AddressingMode::None, }, ); config.network.interfaces.insert( "inter2".to_string(), - NetworkInterface { + definitions::network::NetworkInterface { alias: "test2".to_owned(), comment: "test comment".to_owned(), - interface_type: NetworkInterfaceType::Hardware { + interface_type: definitions::network::NetworkInterfaceType::Hardware { device: "eth0".to_owned(), }, - addressing_mode: AddressingMode::Static { + addressing_mode: definitions::network::AddressingMode::Static { address: "192.168.1.1".to_owned(), }, }, ); - config.network.static_routes.push(StaticRoute { - name: "test1".to_string(), - interface: "eth0".to_string(), - gateway: "192.168.1.1".parse().unwrap(), - destination: "10.42.42.0/24".parse().unwrap(), - metric: 0, - }); + config + .network + .static_routes + .push(definitions::network::StaticRoute { + name: "test1".to_string(), + interface: "eth0".to_string(), + gateway: "192.168.1.1".parse().unwrap(), + destination: "10.42.42.0/24".parse().unwrap(), + metric: 0, + }); let serialized = serde_json::to_string_pretty(&config).unwrap(); println!("serialized = {}", serialized); From 7c2210e26cf741d4076d9c7cb03a35347343eba2 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 20 Oct 2023 23:35:51 +0200 Subject: [PATCH 05/29] Add firewall, service, system and vpn definitions --- Cargo.lock | 10 ++++++ Cargo.toml | 3 +- src/definitions.rs | 8 +++++ src/definitions/firewall.rs | 61 +++++++++++++++++++++++++++++++++ src/definitions/service.rs | 67 +++++++++++++++++++++++++++++++++++++ src/definitions/system.rs | 15 +++++++++ src/definitions/vpn.rs | 33 ++++++++++++++++++ 7 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 src/definitions/firewall.rs create mode 100644 src/definitions/service.rs create mode 100644 src/definitions/system.rs create mode 100644 src/definitions/vpn.rs diff --git a/Cargo.lock b/Cargo.lock index 072cd4a..2cbb9a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,15 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "macaddr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baee0bbc17ce759db233beb01648088061bf678383130602a298e6998eedb2d8" +dependencies = [ + "serde", +] + [[package]] name = "matches" version = "0.1.10" @@ -85,6 +94,7 @@ name = "nfsense" version = "0.1.0" dependencies = [ "ipnet", + "macaddr", "serde", "serde_json", "validator", diff --git a/Cargo.toml b/Cargo.toml index 94e8aa2..c5915e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] ipnet = { version = "2.8.0", features = ["serde"] } +macaddr = { version = "1.0.1", features = ["serde"] } serde = { version = "1.0.189", features = ["derive"] } serde_json = "1.0.107" -validator = { version = "0.15", features = ["derive"] } \ No newline at end of file +validator = { version = "0.15", features = ["derive"] } diff --git a/src/definitions.rs b/src/definitions.rs index a1dc9c8..6acd1d6 100644 --- a/src/definitions.rs +++ b/src/definitions.rs @@ -1,5 +1,9 @@ +pub mod firewall; pub mod network; pub mod object; +pub mod service; +pub mod system; +pub mod vpn; use serde::{Deserialize, Serialize}; use validator::Validate; @@ -9,4 +13,8 @@ pub struct Config { pub config_version: u64, pub network: network::Network, pub object: object::Object, + pub system: system::System, + pub service: service::Service, + pub vpn: vpn::VPN, + pub firewall: firewall::Firewall, } diff --git a/src/definitions/firewall.rs b/src/definitions/firewall.rs new file mode 100644 index 0000000..8633795 --- /dev/null +++ b/src/definitions/firewall.rs @@ -0,0 +1,61 @@ +use serde::{Deserialize, Serialize}; +use validator::Validate; + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct Firewall { + forward_rules: Vec, + destination_nat_rules: Vec, + source_nat_rules: Vec, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct ForwardRule { + pub name: String, + pub services: Vec, + pub source_addresses: Vec, + pub destination_addresses: Vec, + pub comment: String, + pub counter: bool, + pub verdict: Verdict, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct DestinationNATRule { + pub name: String, + pub services: Vec, + pub source_addresses: Vec, + pub destination_addresses: Vec, + pub comment: String, + pub counter: bool, + pub dnat_address: String, + pub dnat_service: String, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct SourceNATRule { + pub name: String, + pub services: Vec, + pub source_addresses: Vec, + pub destination_addresses: Vec, + pub comment: String, + pub counter: bool, + pub snat_type: SNATType, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum Verdict { + Accept, + Drop, + Continue, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum SNATType { + SNAT { + snat_address: String, + snat_service: String, + }, + Masquerade, +} diff --git a/src/definitions/service.rs b/src/definitions/service.rs new file mode 100644 index 0000000..9e07e3d --- /dev/null +++ b/src/definitions/service.rs @@ -0,0 +1,67 @@ +use core::time; +use macaddr::MacAddr8; +use serde::{Deserialize, Serialize}; +use std::net::IpAddr; +use validator::Validate; + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct Service { + pub dhcp_servers: Vec, + pub dns_servers: Vec, + pub ntp_servers: Vec, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct DHCPServer { + pub interface: String, + pub pool: Vec, + pub lease_time: time::Duration, + pub gateway_mode: GatewayMode, + pub dns_server_mode: DNSServerMode, + pub ntp_server_mode: NTPServerMode, + pub reservations: Vec, + pub comment: String, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct DNSServer { + pub interface: String, + pub comment: String, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct NTPServer { + pub interface: String, + pub comment: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum GatewayMode { + None, + Interface, + Specify { gateway: String }, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum DNSServerMode { + None, + Interface, + Specify { dns_servers: Vec }, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum NTPServerMode { + None, + Interface, + Specify { ntp_servers: Vec }, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Reservation { + pub ip_address: IpAddr, + pub hardware_address: MacAddr8, + pub comment: String, +} diff --git a/src/definitions/system.rs b/src/definitions/system.rs new file mode 100644 index 0000000..7fa899b --- /dev/null +++ b/src/definitions/system.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use validator::Validate; + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct System { + pub users: HashMap, +} + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct User { + pub comment: String, + pub hash: String, + pub salt: String, +} diff --git a/src/definitions/vpn.rs b/src/definitions/vpn.rs new file mode 100644 index 0000000..f8a6452 --- /dev/null +++ b/src/definitions/vpn.rs @@ -0,0 +1,33 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use validator::Validate; + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct VPN { + pub wireguard: Wireguard, +} + +#[derive(Serialize, Deserialize, Validate, Default, Debug)] +pub struct Wireguard { + pub interfaces: HashMap, + pub peers: HashMap, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct WireguardInterface { + pub public_key: String, + pub private_key: String, + pub listen_port: u64, + pub peers: Vec, + pub comment: String, +} + +#[derive(Serialize, Deserialize, Validate, Debug)] +pub struct WireguardPeer { + pub public_key: String, + pub preshared_key: Option, + pub allowed_ips: Vec, + pub endpoint: Option, + pub persistent_keepalive: Option, + pub comment: String, +} From bb1b4d78e49dd59f201bf91a3bbf29dd2e8e2f0b Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 21 Oct 2023 23:30:15 +0200 Subject: [PATCH 06/29] Add Clone Traits --- src/definitions/firewall.rs | 12 ++++++------ src/definitions/network.rs | 10 +++++----- src/definitions/object.rs | 10 +++++----- src/definitions/service.rs | 16 ++++++++-------- src/definitions/system.rs | 4 ++-- src/definitions/vpn.rs | 8 ++++---- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/definitions/firewall.rs b/src/definitions/firewall.rs index 8633795..4116b18 100644 --- a/src/definitions/firewall.rs +++ b/src/definitions/firewall.rs @@ -1,14 +1,14 @@ use serde::{Deserialize, Serialize}; use validator::Validate; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Firewall { forward_rules: Vec, destination_nat_rules: Vec, source_nat_rules: Vec, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct ForwardRule { pub name: String, pub services: Vec, @@ -19,7 +19,7 @@ pub struct ForwardRule { pub verdict: Verdict, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct DestinationNATRule { pub name: String, pub services: Vec, @@ -31,7 +31,7 @@ pub struct DestinationNATRule { pub dnat_service: String, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct SourceNATRule { pub name: String, pub services: Vec, @@ -42,7 +42,7 @@ pub struct SourceNATRule { pub snat_type: SNATType, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum Verdict { Accept, @@ -50,7 +50,7 @@ pub enum Verdict { Continue, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum SNATType { SNAT { diff --git a/src/definitions/network.rs b/src/definitions/network.rs index 0a8aa53..498b491 100644 --- a/src/definitions/network.rs +++ b/src/definitions/network.rs @@ -3,13 +3,13 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, net::IpAddr}; use validator::Validate; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Network { pub interfaces: HashMap, pub static_routes: Vec, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct NetworkInterface { pub alias: String, pub comment: String, @@ -17,7 +17,7 @@ pub struct NetworkInterface { pub addressing_mode: AddressingMode, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum NetworkInterfaceType { Hardware { device: String }, @@ -26,7 +26,7 @@ pub enum NetworkInterfaceType { Bridge { members: Vec }, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum AddressingMode { None, @@ -34,7 +34,7 @@ pub enum AddressingMode { DHCP, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct StaticRoute { pub name: String, pub interface: String, diff --git a/src/definitions/object.rs b/src/definitions/object.rs index 123c581..59f7758 100644 --- a/src/definitions/object.rs +++ b/src/definitions/object.rs @@ -3,19 +3,19 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, net::IpAddr}; use validator::Validate; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Object { pub addresses: HashMap, pub services: HashMap, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct Address { pub address_type: AddressType, pub comment: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum AddressType { Host { host: String }, @@ -24,13 +24,13 @@ pub enum AddressType { Group { children: Vec }, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct Service { pub service_type: ServiceType, pub comment: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum ServiceType { TCP { diff --git a/src/definitions/service.rs b/src/definitions/service.rs index 9e07e3d..ea27f2f 100644 --- a/src/definitions/service.rs +++ b/src/definitions/service.rs @@ -4,14 +4,14 @@ use serde::{Deserialize, Serialize}; use std::net::IpAddr; use validator::Validate; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Service { pub dhcp_servers: Vec, pub dns_servers: Vec, pub ntp_servers: Vec, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct DHCPServer { pub interface: String, pub pool: Vec, @@ -23,19 +23,19 @@ pub struct DHCPServer { pub comment: String, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct DNSServer { pub interface: String, pub comment: String, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct NTPServer { pub interface: String, pub comment: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum GatewayMode { None, @@ -43,7 +43,7 @@ pub enum GatewayMode { Specify { gateway: String }, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum DNSServerMode { None, @@ -51,7 +51,7 @@ pub enum DNSServerMode { Specify { dns_servers: Vec }, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum NTPServerMode { None, @@ -59,7 +59,7 @@ pub enum NTPServerMode { Specify { ntp_servers: Vec }, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Reservation { pub ip_address: IpAddr, pub hardware_address: MacAddr8, diff --git a/src/definitions/system.rs b/src/definitions/system.rs index 7fa899b..f0e4384 100644 --- a/src/definitions/system.rs +++ b/src/definitions/system.rs @@ -2,12 +2,12 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use validator::Validate; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct System { pub users: HashMap, } -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct User { pub comment: String, pub hash: String, diff --git a/src/definitions/vpn.rs b/src/definitions/vpn.rs index f8a6452..84104f7 100644 --- a/src/definitions/vpn.rs +++ b/src/definitions/vpn.rs @@ -2,18 +2,18 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use validator::Validate; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct VPN { pub wireguard: Wireguard, } -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Wireguard { pub interfaces: HashMap, pub peers: HashMap, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct WireguardInterface { pub public_key: String, pub private_key: String, @@ -22,7 +22,7 @@ pub struct WireguardInterface { pub comment: String, } -#[derive(Serialize, Deserialize, Validate, Debug)] +#[derive(Serialize, Deserialize, Clone, Validate, Debug)] pub struct WireguardPeer { pub public_key: String, pub preshared_key: Option, From c17e076bc84c0733abbc610a53997e0e16bec4e4 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 21 Oct 2023 23:30:34 +0200 Subject: [PATCH 07/29] make rules public --- src/definitions/firewall.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/definitions/firewall.rs b/src/definitions/firewall.rs index 4116b18..81b9b01 100644 --- a/src/definitions/firewall.rs +++ b/src/definitions/firewall.rs @@ -3,9 +3,9 @@ use validator::Validate; #[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Firewall { - forward_rules: Vec, - destination_nat_rules: Vec, - source_nat_rules: Vec, + pub forward_rules: Vec, + pub destination_nat_rules: Vec, + pub source_nat_rules: Vec, } #[derive(Serialize, Deserialize, Clone, Validate, Debug)] From 030555e72104e3c4b701f7b59109de831471a647 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 21 Oct 2023 23:33:38 +0200 Subject: [PATCH 08/29] move module --- src/{definitions.rs => definitions/config.rs} | 16 ++++++++-------- src/definitions/mod.rs | 7 +++++++ 2 files changed, 15 insertions(+), 8 deletions(-) rename src/{definitions.rs => definitions/config.rs} (63%) create mode 100644 src/definitions/mod.rs diff --git a/src/definitions.rs b/src/definitions/config.rs similarity index 63% rename from src/definitions.rs rename to src/definitions/config.rs index 6acd1d6..0545196 100644 --- a/src/definitions.rs +++ b/src/definitions/config.rs @@ -1,14 +1,14 @@ -pub mod firewall; -pub mod network; -pub mod object; -pub mod service; -pub mod system; -pub mod vpn; - use serde::{Deserialize, Serialize}; use validator::Validate; -#[derive(Serialize, Deserialize, Validate, Default, Debug)] +use super::firewall; +use super::network; +use super::object; +use super::service; +use super::system; +use super::vpn; + +#[derive(Serialize, Deserialize, Clone, Validate, Default, Debug)] pub struct Config { pub config_version: u64, pub network: network::Network, diff --git a/src/definitions/mod.rs b/src/definitions/mod.rs new file mode 100644 index 0000000..7909971 --- /dev/null +++ b/src/definitions/mod.rs @@ -0,0 +1,7 @@ +pub mod config; +pub mod firewall; +pub mod network; +pub mod object; +pub mod service; +pub mod system; +pub mod vpn; From c85a995f9b3bf6dd2dba78b3072e63ec1dac3519 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 21 Oct 2023 23:33:49 +0200 Subject: [PATCH 09/29] ignore json files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ea8c4bf..cb7f85d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.json \ No newline at end of file From 3247828672f4c42c7c59ddf14854f7275a550a0b Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 21 Oct 2023 23:34:27 +0200 Subject: [PATCH 10/29] add config manager --- src/config_manager.rs | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/config_manager.rs diff --git a/src/config_manager.rs b/src/config_manager.rs new file mode 100644 index 0000000..6e1cbc9 --- /dev/null +++ b/src/config_manager.rs @@ -0,0 +1,82 @@ +use validator::Validate; + +use super::definitions::config::Config; +use std::error::Error; +use std::fs; + +const CURRENT_CONFIG_PATH: &str = "config.json"; +const PENDING_CONFIG_PATH: &str = "pending.json"; + +pub struct ConfigManager { + current_config: Config, + pending_config: Config, +} + +impl ConfigManager { + pub fn get_current_config(&self) -> Config { + self.current_config.clone() + } + + pub fn get_pending_config(&self) -> Config { + self.pending_config.clone() + } + + pub fn apply_pending_changes(&mut self) -> Result<(), Box> { + // TODO run Apply functions, revert on failure + write_config_to_file(CURRENT_CONFIG_PATH, self.pending_config.clone())?; + // Also revert if config save fails + self.current_config = self.pending_config.clone(); + Ok(()) + } + + pub fn discard_pending_changes(&mut self) -> Result<(), Box> { + self.pending_config = self.current_config.clone(); + Ok(()) + } + + pub fn start_transaction(&mut self) -> Result> { + Ok(ConfigTransaction { + finished: false, + //guard: guard, + changes: self.pending_config.clone(), + config_manager: self, + }) + } +} + +pub struct ConfigTransaction<'a> { + finished: bool, + config_manager: &'a mut ConfigManager, + pub changes: Config, +} + +impl<'a> ConfigTransaction<'a> { + pub fn commit(&mut self) -> Result<(), Box> { + let ch = self.changes.clone(); + ch.validate()?; + self.config_manager.pending_config = ch.clone(); + Ok(()) + } +} + +pub fn new_config_manager() -> Result> { + Ok(ConfigManager { + current_config: read_file_to_config(CURRENT_CONFIG_PATH)?, + pending_config: read_file_to_config(PENDING_CONFIG_PATH)?, + }) +} + +fn read_file_to_config(path: &str) -> Result> { + let data = fs::read_to_string(path)?; + let conf: Config = serde_json::from_str(&data)?; + if conf.config_version != 1 { + // return Err("Unsupported config Version"); + } + Ok(conf) +} + +fn write_config_to_file(path: &str, conf: Config) -> Result<(), Box> { + let data: String = serde_json::to_string_pretty(&conf)?; + fs::write(path, data)?; + Ok(()) +} From 578251417999c9f7693a7a7919d013137728fa90 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 21 Oct 2023 23:34:45 +0200 Subject: [PATCH 11/29] config manager testing --- src/main.rs | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0a85a13..96f3cef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,37 @@ #![allow(dead_code)] +mod config_manager; mod definitions; -use definitions::Config; +use definitions::config::Config; fn main() { println!("Hello, world!"); - let mut config = Config::default(); - config.config_version = 1; + let mut config_manager = config_manager::new_config_manager().unwrap(); - config.network.interfaces.insert( + let mut tx = config_manager.start_transaction().unwrap(); + + tx.changes + .firewall + .forward_rules + .push(definitions::firewall::ForwardRule { + name: "name".to_string(), + comment: "test".to_string(), + counter: true, + verdict: definitions::firewall::Verdict::Accept, + services: Vec::new(), + destination_addresses: Vec::new(), + source_addresses: Vec::new(), + }); + + tx.commit().unwrap(); + + config_manager.apply_pending_changes().unwrap(); + + let mut tx2 = config_manager.start_transaction().unwrap(); + + tx2.changes.network.interfaces.insert( "inter1".to_string(), definitions::network::NetworkInterface { alias: "test".to_owned(), @@ -22,7 +43,7 @@ fn main() { }, ); - config.network.interfaces.insert( + tx2.changes.network.interfaces.insert( "inter2".to_string(), definitions::network::NetworkInterface { alias: "test2".to_owned(), @@ -36,7 +57,7 @@ fn main() { }, ); - config + tx2.changes .network .static_routes .push(definitions::network::StaticRoute { @@ -47,9 +68,10 @@ fn main() { metric: 0, }); - let serialized = serde_json::to_string_pretty(&config).unwrap(); - println!("serialized = {}", serialized); + tx2.commit().unwrap(); - let deserialized: Config = serde_json::from_str(&serialized).unwrap(); - println!("deserialized = {:?}", deserialized); + config_manager.apply_pending_changes().unwrap(); + + let applied_config = config_manager.get_current_config(); + println!("applied_config = {:#?}", applied_config); } From 65bdbbbb53d55d0591751ef9601e12970d541388 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sun, 22 Oct 2023 02:09:40 +0200 Subject: [PATCH 12/29] Add tokio, axum and tower-http --- Cargo.lock | 675 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + src/main.rs | 30 ++- 3 files changed, 705 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2cbb9a1..d8b627e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.1.2" @@ -11,6 +26,126 @@ dependencies = [ "memchr", ] +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -20,6 +155,114 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "idna" version = "0.2.3" @@ -68,6 +311,28 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "macaddr" version = "1.0.1" @@ -83,29 +348,144 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + [[package]] name = "nfsense" version = "0.1.0" dependencies = [ + "axum", "ipnet", "macaddr", "serde", "serde_json", + "tokio", + "tower-http", "validator", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -148,6 +528,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "regex" version = "1.10.2" @@ -177,12 +566,30 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.189" @@ -214,6 +621,63 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "syn" version = "1.0.109" @@ -236,6 +700,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "tinyvec" version = "1.6.0" @@ -251,6 +721,108 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.4.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -330,3 +902,106 @@ name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index c5915e7..7c8607b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axum = "0.6.20" ipnet = { version = "2.8.0", features = ["serde"] } macaddr = { version = "1.0.1", features = ["serde"] } serde = { version = "1.0.189", features = ["derive"] } serde_json = "1.0.107" +tokio = { version = "1.33.0", features = ["full"] } +tower-http = "0.4.4" validator = { version = "0.15", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index 96f3cef..f697123 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,37 @@ mod config_manager; mod definitions; -use definitions::config::Config; +use axum::{ + response::IntoResponse, + routing::{get, post}, + Json, Router, +}; -fn main() { - println!("Hello, world!"); +pub async fn health_checker_handler() -> impl IntoResponse { + const MESSAGE: &str = "Hello there"; + + let json_response = serde_json::json!({ + "status": "success", + "message": MESSAGE + }); + + Json(json_response) +} + +#[tokio::main] +async fn main() { + println!("Starting..."); let mut config_manager = config_manager::new_config_manager().unwrap(); + let app = Router::new().route("/health", get(health_checker_handler)); + + println!("Server started successfully"); + axum::Server::bind(&"0.0.0.0:8000".parse().unwrap()) + .serve(app.into_make_service()) + .await + .unwrap(); + let mut tx = config_manager.start_transaction().unwrap(); tx.changes From 2a7c13d612e71010e0249c135754ca357b2779a9 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sun, 22 Oct 2023 22:16:35 +0200 Subject: [PATCH 13/29] Move router to seperate file, add traceing --- Cargo.lock | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/main.rs | 38 +++++++++--------------- src/router.rs | 23 +++++++++++++++ 4 files changed, 120 insertions(+), 24 deletions(-) create mode 100644 src/router.rs diff --git a/Cargo.lock b/Cargo.lock index d8b627e..d5a52a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,9 +397,21 @@ dependencies = [ "serde_json", "tokio", "tower-http", + "tracing", + "tracing-subscriber", "validator", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -425,6 +437,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.1" @@ -643,6 +661,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -706,6 +733,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -805,9 +842,21 @@ checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "tracing-core" version = "0.1.32" @@ -815,6 +864,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", ] [[package]] @@ -897,6 +972,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 7c8607b..d4d2d94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,6 @@ serde = { version = "1.0.189", features = ["derive"] } serde_json = "1.0.107" tokio = { version = "1.33.0", features = ["full"] } tower-http = "0.4.4" +tracing = "0.1.40" +tracing-subscriber = "0.3.17" validator = { version = "0.15", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index f697123..0bc8863 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,39 +1,28 @@ #![allow(dead_code)] +use tracing::info; +use tracing_subscriber; + mod config_manager; mod definitions; - -use axum::{ - response::IntoResponse, - routing::{get, post}, - Json, Router, -}; - -pub async fn health_checker_handler() -> impl IntoResponse { - const MESSAGE: &str = "Hello there"; - - let json_response = serde_json::json!({ - "status": "success", - "message": MESSAGE - }); - - Json(json_response) -} +mod router; #[tokio::main] async fn main() { - println!("Starting..."); + tracing_subscriber::fmt::init(); + info!("Starting..."); - let mut config_manager = config_manager::new_config_manager().unwrap(); + // let mut config_manager = config_manager::new_config_manager().unwrap(); - let app = Router::new().route("/health", get(health_checker_handler)); + let app = router::get_router(); - println!("Server started successfully"); - axum::Server::bind(&"0.0.0.0:8000".parse().unwrap()) - .serve(app.into_make_service()) + info!("Server started successfully"); + axum::Server::bind(&"[::]:8080".parse().unwrap()) + .serve(app.await.into_make_service()) .await .unwrap(); + /* let mut tx = config_manager.start_transaction().unwrap(); tx.changes @@ -97,5 +86,6 @@ async fn main() { config_manager.apply_pending_changes().unwrap(); let applied_config = config_manager.get_current_config(); - println!("applied_config = {:#?}", applied_config); + info!("applied_config = {:#?}", applied_config); + */ } diff --git a/src/router.rs b/src/router.rs new file mode 100644 index 0000000..f13fcf2 --- /dev/null +++ b/src/router.rs @@ -0,0 +1,23 @@ +use axum::{ + response::IntoResponse, + routing::{get, post}, + Json, Router, +}; + +use tracing::info; + +pub async fn get_router() -> Router { + Router::new().route("/health", get(health_checker_handler)) +} + +pub async fn health_checker_handler() -> impl IntoResponse { + info!("health hit"); + const MESSAGE: &str = "Hello there"; + + let json_response = serde_json::json!({ + "status": "success", + "message": MESSAGE + }); + + Json(json_response) +} From ed6d1fa3f0ece3cc41b8573d706f418f7a02714a Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 23 Oct 2023 23:51:34 +0200 Subject: [PATCH 14/29] wip auth --- .gitignore | 3 +- Cargo.lock | 95 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 6 +-- src/router.rs | 23 ----------- src/web/auth.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++ src/web/mod.rs | 3 ++ src/web/router.rs | 15 ++++++++ src/web/rpc.rs | 5 +++ 9 files changed, 221 insertions(+), 27 deletions(-) delete mode 100644 src/router.rs create mode 100644 src/web/auth.rs create mode 100644 src/web/mod.rs create mode 100644 src/web/router.rs create mode 100644 src/web/rpc.rs diff --git a/.gitignore b/.gitignore index cb7f85d..c800b27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -*.json \ No newline at end of file +*.json +client/* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d5a52a4..41497e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,6 +140,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cookie" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] + [[package]] name = "fnv" version = "1.0.7" @@ -170,6 +190,17 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "futures-task" version = "0.3.28" @@ -183,9 +214,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -396,6 +429,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "tower-cookies", "tower-http", "tracing", "tracing-subscriber", @@ -504,6 +538,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -679,6 +719,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.11.1" @@ -743,6 +792,35 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -804,6 +882,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-cookies" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40f38d941a2ffd8402b36e02ae407637a9caceb693aaf2edc910437db0f36984" +dependencies = [ + "async-trait", + "axum-core", + "cookie", + "futures-util", + "http", + "parking_lot", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-http" version = "0.4.4" diff --git a/Cargo.toml b/Cargo.toml index d4d2d94..8d8a709 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ macaddr = { version = "1.0.1", features = ["serde"] } serde = { version = "1.0.189", features = ["derive"] } serde_json = "1.0.107" tokio = { version = "1.33.0", features = ["full"] } +tower-cookies = "0.9.0" tower-http = "0.4.4" tracing = "0.1.40" tracing-subscriber = "0.3.17" diff --git a/src/main.rs b/src/main.rs index 0bc8863..0ae1674 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use tracing_subscriber; mod config_manager; mod definitions; -mod router; +mod web; #[tokio::main] async fn main() { @@ -14,11 +14,11 @@ async fn main() { // let mut config_manager = config_manager::new_config_manager().unwrap(); - let app = router::get_router(); + let main_router = web::router::get_router(); info!("Server started successfully"); axum::Server::bind(&"[::]:8080".parse().unwrap()) - .serve(app.await.into_make_service()) + .serve(main_router.await.into_make_service()) .await .unwrap(); diff --git a/src/router.rs b/src/router.rs deleted file mode 100644 index f13fcf2..0000000 --- a/src/router.rs +++ /dev/null @@ -1,23 +0,0 @@ -use axum::{ - response::IntoResponse, - routing::{get, post}, - Json, Router, -}; - -use tracing::info; - -pub async fn get_router() -> Router { - Router::new().route("/health", get(health_checker_handler)) -} - -pub async fn health_checker_handler() -> impl IntoResponse { - info!("health hit"); - const MESSAGE: &str = "Hello there"; - - let json_response = serde_json::json!({ - "status": "success", - "message": MESSAGE - }); - - Json(json_response) -} diff --git a/src/web/auth.rs b/src/web/auth.rs new file mode 100644 index 0000000..9c851d0 --- /dev/null +++ b/src/web/auth.rs @@ -0,0 +1,97 @@ +use std::collections::HashMap; +use std::hash::Hash; + +use axum::routing::post; +use axum::{Json, Router}; +use serde::Deserialize; +use tower_cookies::Cookies; + +use axum::{ + extract::Extension, + http::{Request, StatusCode}, + middleware::{self, Next}, + response::{IntoResponse, Response}, +}; + +const SESSION_COOKIE: &str = "session"; + +#[derive(Clone, Debug)] +struct SessionState { + sessions: HashMap, +} + +#[derive(Clone, Debug)] +struct Session { + username: String, + //expires: time, +} + +#[derive(Clone, Debug, Deserialize)] +struct LoginParameters { + username: String, + password: String, +} + +pub fn routes() -> Router { + let state = SessionState { + sessions: HashMap::new(), + }; + + Router::new() + .route("/session", post(session_handler)) + .route("/login", post(login_handler)) + .route("/logout", post(logout_handler)) +} + +async fn session_handler() -> impl IntoResponse { + //return Err(StatusCode::UNAUTHORIZED); + todo!() +} + +async fn login_handler( + cookies: Cookies, + Json(payload): Json, + // mut session_state: SessionState, +) -> impl IntoResponse { + //cookies.add(Cookie::new()); + todo!() +} + +async fn logout_handler( + cookies: Cookies, + // mut session_state: SessionState +) -> impl IntoResponse { + /* + if let Some(session_cookie) = cookies.get(SESSION_COOKIE) { + let session_id = session_cookie.value(); + + // TODO check that sessions_id is a valid uuidv4 + + session_state.sessions.remove(session_id); + cookies.remove(session_cookie); + } + */ + todo!() +} + +pub async fn mw_auth( + cookies: Cookies, + mut req: Request, + next: Next, + // session_state: SessionState, +) -> Result { + /* + if let Some(session_cookie) = cookies.get(SESSION_COOKIE) { + let session_id = session_cookie.value(); + + // TODO check that sessions_id is a valid uuidv4 + + if let Some(session) = session_state.sessions.get(session_id) { + req.extensions_mut().insert(session.clone()); + return Ok(next.run(req).await); + } + } + return Err(StatusCode::UNAUTHORIZED); + */ + todo!() +} diff --git a/src/web/mod.rs b/src/web/mod.rs new file mode 100644 index 0000000..cf24610 --- /dev/null +++ b/src/web/mod.rs @@ -0,0 +1,3 @@ +pub mod auth; +pub mod router; +pub mod rpc; diff --git a/src/web/router.rs b/src/web/router.rs new file mode 100644 index 0000000..171556e --- /dev/null +++ b/src/web/router.rs @@ -0,0 +1,15 @@ +use axum::{middleware, Router}; + +use tower_cookies::CookieManagerLayer; + +#[derive(Clone)] +struct tmp {} + +pub async fn get_router() -> Router { + Router::new() + .merge(super::auth::routes()) + .nest("/api", super::rpc::routes()) + .layer(middleware::from_fn_with_state(tmp {}, super::auth::mw_auth)) + .layer(CookieManagerLayer::new()) + // .fallback_service(service) +} diff --git a/src/web/rpc.rs b/src/web/rpc.rs new file mode 100644 index 0000000..77e3e45 --- /dev/null +++ b/src/web/rpc.rs @@ -0,0 +1,5 @@ +use axum::Router; + +pub fn routes() -> Router { + Router::new() +} From 125c2d48d8ff9a702d379cc6fb5781ffe7ffcc6a Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 25 Oct 2023 18:06:07 +0200 Subject: [PATCH 15/29] Make Config Manager Thread Safe and Cloneable --- src/config_manager.rs | 50 +++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/config_manager.rs b/src/config_manager.rs index 6e1cbc9..590576e 100644 --- a/src/config_manager.rs +++ b/src/config_manager.rs @@ -3,69 +3,83 @@ use validator::Validate; use super::definitions::config::Config; use std::error::Error; use std::fs; +use std::sync::{Arc, Mutex, MutexGuard}; const CURRENT_CONFIG_PATH: &str = "config.json"; const PENDING_CONFIG_PATH: &str = "pending.json"; +#[derive(Clone)] pub struct ConfigManager { + shared_data: Arc>, +} + +struct SharedData { current_config: Config, pending_config: Config, } +// Note, using unwarp on a mutex lock is ok since that only errors with mutex poisoning + impl ConfigManager { + pub fn new() -> Result> { + Ok(Self { + shared_data: Arc::new(Mutex::new(SharedData { + current_config: read_file_to_config(CURRENT_CONFIG_PATH)?, + pending_config: read_file_to_config(PENDING_CONFIG_PATH)?, + })), + }) + } + pub fn get_current_config(&self) -> Config { - self.current_config.clone() + self.shared_data.lock().unwrap().current_config.clone() } pub fn get_pending_config(&self) -> Config { - self.pending_config.clone() + self.shared_data.lock().unwrap().pending_config.clone() } pub fn apply_pending_changes(&mut self) -> Result<(), Box> { + let mut data = self.shared_data.lock().unwrap(); // TODO run Apply functions, revert on failure - write_config_to_file(CURRENT_CONFIG_PATH, self.pending_config.clone())?; + write_config_to_file(CURRENT_CONFIG_PATH, data.pending_config.clone())?; // Also revert if config save fails - self.current_config = self.pending_config.clone(); + data.current_config = data.pending_config.clone(); Ok(()) } pub fn discard_pending_changes(&mut self) -> Result<(), Box> { - self.pending_config = self.current_config.clone(); + let mut data = self.shared_data.lock().unwrap(); + + data.pending_config = data.current_config.clone(); Ok(()) } pub fn start_transaction(&mut self) -> Result> { + let data = self.shared_data.lock().unwrap(); + Ok(ConfigTransaction { finished: false, - //guard: guard, - changes: self.pending_config.clone(), - config_manager: self, + changes: data.pending_config.clone(), + shared_data: data, }) } } pub struct ConfigTransaction<'a> { finished: bool, - config_manager: &'a mut ConfigManager, + shared_data: MutexGuard<'a, SharedData>, pub changes: Config, } impl<'a> ConfigTransaction<'a> { - pub fn commit(&mut self) -> Result<(), Box> { + pub fn commit(mut self) -> Result<(), Box> { let ch = self.changes.clone(); ch.validate()?; - self.config_manager.pending_config = ch.clone(); + self.shared_data.pending_config = ch.clone(); Ok(()) } } -pub fn new_config_manager() -> Result> { - Ok(ConfigManager { - current_config: read_file_to_config(CURRENT_CONFIG_PATH)?, - pending_config: read_file_to_config(PENDING_CONFIG_PATH)?, - }) -} - fn read_file_to_config(path: &str) -> Result> { let data = fs::read_to_string(path)?; let conf: Config = serde_json::from_str(&data)?; From eaf9f2d3b1813074e75ae8ada4c0aae889b68a6c Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 25 Oct 2023 18:13:59 +0200 Subject: [PATCH 16/29] Add TODO's, Save pending config Changes --- src/config_manager.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/config_manager.rs b/src/config_manager.rs index 590576e..a68904a 100644 --- a/src/config_manager.rs +++ b/src/config_manager.rs @@ -25,6 +25,7 @@ impl ConfigManager { Ok(Self { shared_data: Arc::new(Mutex::new(SharedData { current_config: read_file_to_config(CURRENT_CONFIG_PATH)?, + // TODO Dont Fail if pending config is missing, use current instead pending_config: read_file_to_config(PENDING_CONFIG_PATH)?, })), }) @@ -40,15 +41,18 @@ impl ConfigManager { pub fn apply_pending_changes(&mut self) -> Result<(), Box> { let mut data = self.shared_data.lock().unwrap(); - // TODO run Apply functions, revert on failure + // TODO run Apply functions + // TODO Revert on Apply Failure and Return write_config_to_file(CURRENT_CONFIG_PATH, data.pending_config.clone())?; - // Also revert if config save fails + // TODO revert if config save fails + // TODO Remove Pending Config File data.current_config = data.pending_config.clone(); Ok(()) } pub fn discard_pending_changes(&mut self) -> Result<(), Box> { let mut data = self.shared_data.lock().unwrap(); + // TODO Remove Pending Config File data.pending_config = data.current_config.clone(); Ok(()) @@ -76,6 +80,7 @@ impl<'a> ConfigTransaction<'a> { let ch = self.changes.clone(); ch.validate()?; self.shared_data.pending_config = ch.clone(); + write_config_to_file(PENDING_CONFIG_PATH, ch.clone())?; Ok(()) } } From 93c5eca7cdc01c72a77c06c426be0f818d08a347 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 25 Oct 2023 18:42:07 +0200 Subject: [PATCH 17/29] Move Router to main, Setup AppState --- src/main.rs | 33 ++++++++++++++++++++++++++++++--- src/state.rs | 8 ++++++++ src/web/auth.rs | 13 +++++-------- src/web/mod.rs | 1 - src/web/router.rs | 15 --------------- src/web/rpc.rs | 2 +- 6 files changed, 44 insertions(+), 28 deletions(-) create mode 100644 src/state.rs delete mode 100644 src/web/router.rs diff --git a/src/main.rs b/src/main.rs index 0ae1674..8731df7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,21 @@ #![allow(dead_code)] +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; + +use axum::{middleware, Router}; +use config_manager::ConfigManager; +use state::AppState; +use tower_cookies::CookieManagerLayer; use tracing::info; use tracing_subscriber; +use web::auth::SessionState; mod config_manager; mod definitions; +mod state; mod web; #[tokio::main] @@ -12,13 +23,29 @@ async fn main() { tracing_subscriber::fmt::init(); info!("Starting..."); - // let mut config_manager = config_manager::new_config_manager().unwrap(); + // TODO Check Config Manager Setup Error + let config_manager = ConfigManager::new().unwrap(); - let main_router = web::router::get_router(); + let session_state = SessionState { + sessions: Arc::new(RwLock::new(HashMap::new())), + }; + + let app_state = AppState { + config_manager, + session_state, + }; + + let main_router = Router::new() + .merge(web::auth::routes()) + .merge(web::rpc::routes()) + .with_state(app_state) + .layer(middleware::from_fn_with_state((), web::auth::mw_auth)) + .layer(CookieManagerLayer::new()); + // .fallback_service(service) info!("Server started successfully"); axum::Server::bind(&"[::]:8080".parse().unwrap()) - .serve(main_router.await.into_make_service()) + .serve(main_router.into_make_service()) .await .unwrap(); diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..26f5551 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,8 @@ +use super::config_manager::ConfigManager; +use super::web::auth::SessionState; + +#[derive(Clone)] +pub struct AppState { + pub config_manager: ConfigManager, + pub session_state: SessionState, +} diff --git a/src/web/auth.rs b/src/web/auth.rs index 9c851d0..0915bed 100644 --- a/src/web/auth.rs +++ b/src/web/auth.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::hash::Hash; +use std::sync::{Arc, RwLock}; use axum::routing::post; use axum::{Json, Router}; @@ -16,12 +17,12 @@ use axum::{ const SESSION_COOKIE: &str = "session"; #[derive(Clone, Debug)] -struct SessionState { - sessions: HashMap, +pub struct SessionState { + pub sessions: Arc>>, } #[derive(Clone, Debug)] -struct Session { +pub struct Session { username: String, //expires: time, } @@ -32,11 +33,7 @@ struct LoginParameters { password: String, } -pub fn routes() -> Router { - let state = SessionState { - sessions: HashMap::new(), - }; - +pub fn routes() -> Router { Router::new() .route("/session", post(session_handler)) .route("/login", post(login_handler)) diff --git a/src/web/mod.rs b/src/web/mod.rs index cf24610..6a60678 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -1,3 +1,2 @@ pub mod auth; -pub mod router; pub mod rpc; diff --git a/src/web/router.rs b/src/web/router.rs deleted file mode 100644 index 171556e..0000000 --- a/src/web/router.rs +++ /dev/null @@ -1,15 +0,0 @@ -use axum::{middleware, Router}; - -use tower_cookies::CookieManagerLayer; - -#[derive(Clone)] -struct tmp {} - -pub async fn get_router() -> Router { - Router::new() - .merge(super::auth::routes()) - .nest("/api", super::rpc::routes()) - .layer(middleware::from_fn_with_state(tmp {}, super::auth::mw_auth)) - .layer(CookieManagerLayer::new()) - // .fallback_service(service) -} diff --git a/src/web/rpc.rs b/src/web/rpc.rs index 77e3e45..5340a97 100644 --- a/src/web/rpc.rs +++ b/src/web/rpc.rs @@ -1,5 +1,5 @@ use axum::Router; -pub fn routes() -> Router { +pub fn routes() -> Router { Router::new() } From 46e7c143d62775d29d94f5413190e6c0cc22d8a3 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 25 Oct 2023 19:00:11 +0200 Subject: [PATCH 18/29] Distribute Session State --- src/main.rs | 10 +++++++--- src/web/auth.rs | 23 ++++++++++++----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8731df7..678c8f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,14 +32,18 @@ async fn main() { let app_state = AppState { config_manager, - session_state, + session_state: session_state.clone(), }; + // Note: The Router Works Bottom Up, So the auth middleware will only applies to everything above it. let main_router = Router::new() - .merge(web::auth::routes()) .merge(web::rpc::routes()) + .layer(middleware::from_fn_with_state( + session_state, + web::auth::mw_auth, + )) + .merge(web::auth::routes()) .with_state(app_state) - .layer(middleware::from_fn_with_state((), web::auth::mw_auth)) .layer(CookieManagerLayer::new()); // .fallback_service(service) diff --git a/src/web/auth.rs b/src/web/auth.rs index 0915bed..8348573 100644 --- a/src/web/auth.rs +++ b/src/web/auth.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::hash::Hash; use std::sync::{Arc, RwLock}; +use super::super::AppState; use axum::routing::post; use axum::{Json, Router}; use serde::Deserialize; @@ -9,6 +10,7 @@ use tower_cookies::Cookies; use axum::{ extract::Extension, + extract::State, http::{Request, StatusCode}, middleware::{self, Next}, response::{IntoResponse, Response}, @@ -33,20 +35,16 @@ struct LoginParameters { password: String, } -pub fn routes() -> Router { +pub fn routes() -> Router { Router::new() - .route("/session", post(session_handler)) .route("/login", post(login_handler)) .route("/logout", post(logout_handler)) -} - -async fn session_handler() -> impl IntoResponse { - //return Err(StatusCode::UNAUTHORIZED); - todo!() + .route("/session", post(session_handler)) } async fn login_handler( cookies: Cookies, + State(state): State, Json(payload): Json, // mut session_state: SessionState, ) -> impl IntoResponse { @@ -54,10 +52,7 @@ async fn login_handler( todo!() } -async fn logout_handler( - cookies: Cookies, - // mut session_state: SessionState -) -> impl IntoResponse { +async fn logout_handler(cookies: Cookies, app_state: State) -> impl IntoResponse { /* if let Some(session_cookie) = cookies.get(SESSION_COOKIE) { let session_id = session_cookie.value(); @@ -71,7 +66,13 @@ async fn logout_handler( todo!() } +async fn session_handler(cookies: Cookies, State(state): State) -> impl IntoResponse { + //return Err(StatusCode::UNAUTHORIZED); + todo!() +} + pub async fn mw_auth( + app_state: State, cookies: Cookies, mut req: Request, next: Next, From 2f48c42677adf59d4ec8360a2de4da3a3668e5e3 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 25 Oct 2023 19:02:20 +0200 Subject: [PATCH 19/29] Pass AppState instead of Session State --- src/main.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 678c8f3..24f80cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,20 +26,18 @@ async fn main() { // TODO Check Config Manager Setup Error let config_manager = ConfigManager::new().unwrap(); - let session_state = SessionState { - sessions: Arc::new(RwLock::new(HashMap::new())), - }; - let app_state = AppState { config_manager, - session_state: session_state.clone(), + session_state: SessionState { + sessions: Arc::new(RwLock::new(HashMap::new())), + }, }; // Note: The Router Works Bottom Up, So the auth middleware will only applies to everything above it. let main_router = Router::new() .merge(web::rpc::routes()) .layer(middleware::from_fn_with_state( - session_state, + app_state.clone(), web::auth::mw_auth, )) .merge(web::auth::routes()) From cadfd8cca98384b2ce1eed099215d98e37e1449c Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 26 Oct 2023 00:26:40 +0200 Subject: [PATCH 20/29] Add Custom Error --- Cargo.lock | 28 ++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/config_manager.rs | 30 ++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41497e1..38b5c82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,6 +151,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "custom_error" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8a51dd197fa6ba5b4dc98a990a43cc13693c23eb0089ebb0fcc1f04152bca6" + [[package]] name = "deranged" version = "0.3.9" @@ -221,6 +227,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.28.0" @@ -424,6 +441,7 @@ name = "nfsense" version = "0.1.0" dependencies = [ "axum", + "custom_error", "ipnet", "macaddr", "serde", @@ -433,6 +451,7 @@ dependencies = [ "tower-http", "tracing", "tracing-subscriber", + "uuid", "validator", ] @@ -1025,6 +1044,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +dependencies = [ + "getrandom", +] + [[package]] name = "validator" version = "0.15.0" diff --git a/Cargo.toml b/Cargo.toml index 8d8a709..6c776aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] axum = "0.6.20" +custom_error = "1.9.2" ipnet = { version = "2.8.0", features = ["serde"] } macaddr = { version = "1.0.1", features = ["serde"] } serde = { version = "1.0.189", features = ["derive"] } @@ -16,4 +17,5 @@ tower-cookies = "0.9.0" tower-http = "0.4.4" tracing = "0.1.40" tracing-subscriber = "0.3.17" +uuid = { version = "1.5.0", features = ["v4"] } validator = { version = "0.15", features = ["derive"] } diff --git a/src/config_manager.rs b/src/config_manager.rs index a68904a..e17ec81 100644 --- a/src/config_manager.rs +++ b/src/config_manager.rs @@ -1,12 +1,22 @@ +use serde_json::to_string; use validator::Validate; use super::definitions::config::Config; -use std::error::Error; use std::fs; use std::sync::{Arc, Mutex, MutexGuard}; +use std::{io, result::Result}; -const CURRENT_CONFIG_PATH: &str = "config.json"; -const PENDING_CONFIG_PATH: &str = "pending.json"; +use custom_error::custom_error; + +custom_error! { pub ConfigError + IoError{source: io::Error} = "io error", + SerdeError{source: serde_json::Error} = "serde json error", + ValidatonError{source: validator::ValidationErrors} = "validation failed" + +} + +pub const CURRENT_CONFIG_PATH: &str = "config.json"; +pub const PENDING_CONFIG_PATH: &str = "pending.json"; #[derive(Clone)] pub struct ConfigManager { @@ -21,7 +31,7 @@ struct SharedData { // Note, using unwarp on a mutex lock is ok since that only errors with mutex poisoning impl ConfigManager { - pub fn new() -> Result> { + pub fn new() -> Result { Ok(Self { shared_data: Arc::new(Mutex::new(SharedData { current_config: read_file_to_config(CURRENT_CONFIG_PATH)?, @@ -39,7 +49,7 @@ impl ConfigManager { self.shared_data.lock().unwrap().pending_config.clone() } - pub fn apply_pending_changes(&mut self) -> Result<(), Box> { + pub fn apply_pending_changes(&mut self) -> Result<(), ConfigError> { let mut data = self.shared_data.lock().unwrap(); // TODO run Apply functions // TODO Revert on Apply Failure and Return @@ -50,7 +60,7 @@ impl ConfigManager { Ok(()) } - pub fn discard_pending_changes(&mut self) -> Result<(), Box> { + pub fn discard_pending_changes(&mut self) -> Result<(), ConfigError> { let mut data = self.shared_data.lock().unwrap(); // TODO Remove Pending Config File @@ -58,7 +68,7 @@ impl ConfigManager { Ok(()) } - pub fn start_transaction(&mut self) -> Result> { + pub fn start_transaction(&mut self) -> Result { let data = self.shared_data.lock().unwrap(); Ok(ConfigTransaction { @@ -76,7 +86,7 @@ pub struct ConfigTransaction<'a> { } impl<'a> ConfigTransaction<'a> { - pub fn commit(mut self) -> Result<(), Box> { + pub fn commit(mut self) -> Result<(), ConfigError> { let ch = self.changes.clone(); ch.validate()?; self.shared_data.pending_config = ch.clone(); @@ -85,7 +95,7 @@ impl<'a> ConfigTransaction<'a> { } } -fn read_file_to_config(path: &str) -> Result> { +fn read_file_to_config(path: &str) -> Result { let data = fs::read_to_string(path)?; let conf: Config = serde_json::from_str(&data)?; if conf.config_version != 1 { @@ -94,7 +104,7 @@ fn read_file_to_config(path: &str) -> Result> { Ok(conf) } -fn write_config_to_file(path: &str, conf: Config) -> Result<(), Box> { +fn write_config_to_file(path: &str, conf: Config) -> Result<(), ConfigError> { let data: String = serde_json::to_string_pretty(&conf)?; fs::write(path, data)?; Ok(()) From acfa0cc83af33c2a0b31091e077fb3356808ca57 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 26 Oct 2023 00:27:28 +0200 Subject: [PATCH 21/29] Add Default config generation --- src/config_manager.rs | 13 +++++++++++++ src/main.rs | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/config_manager.rs b/src/config_manager.rs index e17ec81..cee98ee 100644 --- a/src/config_manager.rs +++ b/src/config_manager.rs @@ -109,3 +109,16 @@ fn write_config_to_file(path: &str, conf: Config) -> Result<(), ConfigError> { fs::write(path, data)?; Ok(()) } + +pub fn generate_default_config(path: &str) -> Result<(), ConfigError> { + let mut conf = Config::default(); + conf.system.users.insert( + "admin".to_string(), + crate::definitions::system::User { + comment: "Default Admin".to_string(), + hash: "".to_string(), + salt: "".to_string(), + }, + ); + write_config_to_file(path, conf) +} diff --git a/src/main.rs b/src/main.rs index 24f80cb..893b3e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use std::{ use axum::{middleware, Router}; use config_manager::ConfigManager; use state::AppState; +use std::env; use tower_cookies::CookieManagerLayer; use tracing::info; use tracing_subscriber; @@ -23,6 +24,16 @@ async fn main() { tracing_subscriber::fmt::init(); info!("Starting..."); + let args: Vec = env::args().collect(); + + if args.len() > 1 && args[1] == "generate-default" { + info!("Generating default config..."); + config_manager::generate_default_config(config_manager::CURRENT_CONFIG_PATH).unwrap(); + config_manager::generate_default_config(config_manager::PENDING_CONFIG_PATH).unwrap(); + info!("Done! Exiting..."); + return; + } + // TODO Check Config Manager Setup Error let config_manager = ConfigManager::new().unwrap(); From 3a9be114d82f882f777fc54c7453e4c21a32c194 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 26 Oct 2023 00:28:17 +0200 Subject: [PATCH 22/29] get Authentication and Sessions working --- src/web/auth.rs | 106 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 27 deletions(-) diff --git a/src/web/auth.rs b/src/web/auth.rs index 8348573..15d521a 100644 --- a/src/web/auth.rs +++ b/src/web/auth.rs @@ -1,12 +1,14 @@ use std::collections::HashMap; +use std::error::Error; use std::hash::Hash; use std::sync::{Arc, RwLock}; +use uuid::Uuid; use super::super::AppState; use axum::routing::post; use axum::{Json, Router}; use serde::Deserialize; -use tower_cookies::Cookies; +use tower_cookies::{Cookie, Cookies}; use axum::{ extract::Extension, @@ -16,6 +18,13 @@ use axum::{ response::{IntoResponse, Response}, }; +use custom_error::custom_error; + +custom_error! { AuthError + NoSessionCookie = "No Session Cookie Found", + InvalidSession = "Invalid Session" +} + const SESSION_COOKIE: &str = "session"; #[derive(Clone, Debug)] @@ -25,8 +34,10 @@ pub struct SessionState { #[derive(Clone, Debug)] pub struct Session { - username: String, + pub username: String, //expires: time, + // TODO have permissions here for fast access, update Permissions with a config manager apply function + // permissions } #[derive(Clone, Debug, Deserialize)] @@ -46,50 +57,91 @@ async fn login_handler( cookies: Cookies, State(state): State, Json(payload): Json, - // mut session_state: SessionState, ) -> impl IntoResponse { - //cookies.add(Cookie::new()); - todo!() + if let Some(user) = state + .config_manager + .get_current_config() + .system + .users + .get(&payload.username.to_string()) + { + // TODO use propper hash algorithm compatible with /etc/passwd + if payload.password == "nfsense" { + let mut sessions = state.session_state.sessions.write().unwrap(); + let id = Uuid::new_v4().to_string(); + + sessions.insert( + id.clone(), + Session { + username: payload.username, + }, + ); + + cookies.add(Cookie::new(SESSION_COOKIE, id)); + return StatusCode::OK; + } + } + StatusCode::UNAUTHORIZED } -async fn logout_handler(cookies: Cookies, app_state: State) -> impl IntoResponse { - /* - if let Some(session_cookie) = cookies.get(SESSION_COOKIE) { - let session_id = session_cookie.value(); +async fn logout_handler(cookies: Cookies, state: State) -> impl IntoResponse { + let session_cookie = cookies.get(SESSION_COOKIE); + match session_cookie { + Some(s) => { + let session_id = s.value(); - // TODO check that sessions_id is a valid uuidv4 + let mut sessions = state.session_state.sessions.write().unwrap(); - session_state.sessions.remove(session_id); - cookies.remove(session_cookie); + // TODO Fix Cookie remove + // cookies.remove(s.clone()); + + if let Some(session) = sessions.get(session_id) { + sessions.remove(session_id); + return StatusCode::OK; + } + return StatusCode::UNAUTHORIZED; + } + None => return StatusCode::UNAUTHORIZED, + } +} + +fn get_session(cookies: Cookies, state: SessionState) -> Result { + let session_cookie = cookies.get(SESSION_COOKIE); + match session_cookie { + Some(s) => { + let session_id = s.value(); + + let sessions = state.sessions.write().unwrap(); + + if let Some(session) = sessions.get(session_id) { + return Ok(session.clone()); + } + return Err(AuthError::InvalidSession); + } + None => return Err(AuthError::NoSessionCookie), } - */ - todo!() } async fn session_handler(cookies: Cookies, State(state): State) -> impl IntoResponse { - //return Err(StatusCode::UNAUTHORIZED); - todo!() + match get_session(cookies, state.session_state) { + // TODO Return build git commit hash as json result for frontend reloading + Ok(_) => return StatusCode::OK, + Err(_) => return StatusCode::UNAUTHORIZED, + } } pub async fn mw_auth( - app_state: State, + state: State, cookies: Cookies, mut req: Request, next: Next, // session_state: SessionState, ) -> Result { - /* - if let Some(session_cookie) = cookies.get(SESSION_COOKIE) { - let session_id = session_cookie.value(); - - // TODO check that sessions_id is a valid uuidv4 - - if let Some(session) = session_state.sessions.get(session_id) { + match get_session(cookies, state.session_state.clone()) { + Ok(session) => { req.extensions_mut().insert(session.clone()); return Ok(next.run(req).await); } + Err(_) => return Err(StatusCode::UNAUTHORIZED), } - return Err(StatusCode::UNAUTHORIZED); - */ - todo!() } From faf446d93a9cb243b2a5cfde121c08593b41c989 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 26 Oct 2023 00:28:44 +0200 Subject: [PATCH 23/29] Add temp api handle --- src/web/rpc.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/web/rpc.rs b/src/web/rpc.rs index 5340a97..daae823 100644 --- a/src/web/rpc.rs +++ b/src/web/rpc.rs @@ -1,5 +1,26 @@ -use axum::Router; +use super::super::AppState; +use axum::routing::post; +use axum::{Json, Router}; +use tower_cookies::{Cookie, Cookies}; + +use axum::{ + extract::Extension, + extract::State, + http::{Request, StatusCode}, + middleware::{self, Next}, + response::{IntoResponse, Response}, +}; + +use tracing::info; pub fn routes() -> Router { - Router::new() + Router::new().route("/api", post(api_handler)) +} + +async fn api_handler( + State(state): State, + session: Extension, +) -> impl IntoResponse { + info!("api hit! user: {:?}", session.username); + StatusCode::NOT_IMPLEMENTED } From 4c3ce8208a316958c2c845fc44a842374f9fb459 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 26 Oct 2023 13:08:17 +0200 Subject: [PATCH 24/29] Add shadow compatible password hashing --- Cargo.lock | 190 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/config_manager.rs | 15 +-- src/definitions/system.rs | 1 - src/main.rs | 7 +- src/web/auth.rs | 12 ++- 6 files changed, 215 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38b5c82..b241c0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,6 +119,32 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blowfish" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32fa6a061124e37baba002e496d203e23ba3d7b73750be82dbfbc92913048a5b" +dependencies = [ + "byteorder", + "cipher", + "opaque-debug", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.5.0" @@ -140,6 +166,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + [[package]] name = "cookie" version = "0.17.0" @@ -151,6 +186,25 @@ dependencies = [ "version_check", ] +[[package]] +name = "cpufeatures" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "custom_error" version = "1.9.2" @@ -166,6 +220,15 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "fnv" version = "1.0.7" @@ -227,6 +290,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -250,6 +323,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest", +] + [[package]] name = "http" version = "0.2.9" @@ -404,6 +487,17 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer", + "digest", + "opaque-debug", +] + [[package]] name = "memchr" version = "2.6.4" @@ -444,6 +538,7 @@ dependencies = [ "custom_error", "ipnet", "macaddr", + "pwhash", "serde", "serde_json", "tokio", @@ -490,6 +585,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "overload" version = "0.1.1" @@ -563,6 +664,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -596,6 +703,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pwhash" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419a3ad8fa9f9d445e69d9b185a24878ae6e6f55c96e4512f4a0e28cd3bc5c56" +dependencies = [ + "blowfish", + "byteorder", + "hmac", + "md-5", + "rand", + "sha-1", + "sha2", +] + [[package]] name = "quote" version = "1.0.33" @@ -605,6 +727,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -720,6 +872,32 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -773,6 +951,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.109" @@ -1012,6 +1196,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.13" diff --git a/Cargo.toml b/Cargo.toml index 6c776aa..d27ab4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ axum = "0.6.20" custom_error = "1.9.2" ipnet = { version = "2.8.0", features = ["serde"] } macaddr = { version = "1.0.1", features = ["serde"] } +pwhash = "1.0.0" serde = { version = "1.0.189", features = ["derive"] } serde_json = "1.0.107" tokio = { version = "1.33.0", features = ["full"] } diff --git a/src/config_manager.rs b/src/config_manager.rs index cee98ee..79c2db2 100644 --- a/src/config_manager.rs +++ b/src/config_manager.rs @@ -1,4 +1,3 @@ -use serde_json::to_string; use validator::Validate; use super::definitions::config::Config; @@ -8,11 +7,14 @@ use std::{io, result::Result}; use custom_error::custom_error; +use pwhash::sha512_crypt; + custom_error! { pub ConfigError IoError{source: io::Error} = "io error", SerdeError{source: serde_json::Error} = "serde json error", - ValidatonError{source: validator::ValidationErrors} = "validation failed" - + ValidatonError{source: validator::ValidationErrors} = "validation failed", + HashError{source: pwhash::error::Error} = "password hash generation", + UnsupportedVersionError = "unsupported config version", } pub const CURRENT_CONFIG_PATH: &str = "config.json"; @@ -99,7 +101,7 @@ fn read_file_to_config(path: &str) -> Result { let data = fs::read_to_string(path)?; let conf: Config = serde_json::from_str(&data)?; if conf.config_version != 1 { - // return Err("Unsupported config Version"); + return Err(ConfigError::UnsupportedVersionError); } Ok(conf) } @@ -112,12 +114,13 @@ fn write_config_to_file(path: &str, conf: Config) -> Result<(), ConfigError> { pub fn generate_default_config(path: &str) -> Result<(), ConfigError> { let mut conf = Config::default(); + let hash = sha512_crypt::hash("nfsense")?; + conf.config_version = 1; conf.system.users.insert( "admin".to_string(), crate::definitions::system::User { comment: "Default Admin".to_string(), - hash: "".to_string(), - salt: "".to_string(), + hash: hash, }, ); write_config_to_file(path, conf) diff --git a/src/definitions/system.rs b/src/definitions/system.rs index f0e4384..48f01ff 100644 --- a/src/definitions/system.rs +++ b/src/definitions/system.rs @@ -11,5 +11,4 @@ pub struct System { pub struct User { pub comment: String, pub hash: String, - pub salt: String, } diff --git a/src/main.rs b/src/main.rs index 893b3e6..cf69770 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use axum::{middleware, Router}; use config_manager::ConfigManager; use state::AppState; use std::env; +use std::fs; use tower_cookies::CookieManagerLayer; use tracing::info; use tracing_subscriber; @@ -29,7 +30,11 @@ async fn main() { if args.len() > 1 && args[1] == "generate-default" { info!("Generating default config..."); config_manager::generate_default_config(config_manager::CURRENT_CONFIG_PATH).unwrap(); - config_manager::generate_default_config(config_manager::PENDING_CONFIG_PATH).unwrap(); + fs::copy( + config_manager::CURRENT_CONFIG_PATH, + config_manager::PENDING_CONFIG_PATH, + ) + .unwrap(); info!("Done! Exiting..."); return; } diff --git a/src/web/auth.rs b/src/web/auth.rs index 15d521a..526c1a2 100644 --- a/src/web/auth.rs +++ b/src/web/auth.rs @@ -18,6 +18,9 @@ use axum::{ response::{IntoResponse, Response}, }; +use pwhash::sha512_crypt; +use tracing::info; + use custom_error::custom_error; custom_error! { AuthError @@ -65,22 +68,24 @@ async fn login_handler( .users .get(&payload.username.to_string()) { - // TODO use propper hash algorithm compatible with /etc/passwd - if payload.password == "nfsense" { + if sha512_crypt::verify(payload.password, &user.hash) { let mut sessions = state.session_state.sessions.write().unwrap(); let id = Uuid::new_v4().to_string(); sessions.insert( id.clone(), Session { - username: payload.username, + username: payload.username.clone(), }, ); cookies.add(Cookie::new(SESSION_COOKIE, id)); + info!("user logged in: {:?}", payload.username); return StatusCode::OK; } } + + info!("user login failed: {:?}", payload.username); StatusCode::UNAUTHORIZED } @@ -96,6 +101,7 @@ async fn logout_handler(cookies: Cookies, state: State) -> impl IntoRe // cookies.remove(s.clone()); if let Some(session) = sessions.get(session_id) { + info!("user logged out: {:?}", session.username); sessions.remove(session_id); return StatusCode::OK; } From 9090f883af278c5001d8afcbfb7250805b6389d1 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 26 Oct 2023 17:31:42 +0200 Subject: [PATCH 25/29] get jsonrpc working --- Cargo.lock | 22 +++++ Cargo.toml | 2 + src/json_rpc/error.rs | 97 +++++++++++++++++++++ src/json_rpc/mod.rs | 191 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/web/rpc.rs | 20 ++++- 6 files changed, 331 insertions(+), 2 deletions(-) create mode 100644 src/json_rpc/error.rs create mode 100644 src/json_rpc/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b241c0e..87372cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,6 +534,7 @@ dependencies = [ name = "nfsense" version = "0.1.0" dependencies = [ + "async-trait", "axum", "custom_error", "ipnet", @@ -541,6 +542,7 @@ dependencies = [ "pwhash", "serde", "serde_json", + "thiserror", "tokio", "tower-cookies", "tower-http", @@ -985,6 +987,26 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "thread_local" version = "1.1.7" diff --git a/Cargo.toml b/Cargo.toml index d27ab4d..4a65bef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-trait = "0.1.74" axum = "0.6.20" custom_error = "1.9.2" ipnet = { version = "2.8.0", features = ["serde"] } @@ -13,6 +14,7 @@ macaddr = { version = "1.0.1", features = ["serde"] } pwhash = "1.0.0" serde = { version = "1.0.189", features = ["derive"] } serde_json = "1.0.107" +thiserror = "1.0.50" tokio = { version = "1.33.0", features = ["full"] } tower-cookies = "0.9.0" tower-http = "0.4.4" diff --git a/src/json_rpc/error.rs b/src/json_rpc/error.rs new file mode 100644 index 0000000..21fb2d8 --- /dev/null +++ b/src/json_rpc/error.rs @@ -0,0 +1,97 @@ +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +/// Constants for [error object](https://www.jsonrpc.org/specification#error_object) +pub const INVALID_REQUEST: i32 = -32600; +pub const METHOD_NOT_FOUND: i32 = -32601; +pub const INVALID_PARAMS: i32 = -32602; +pub const INTERNAL_ERROR: i32 = -32603; +pub const PARSE_ERROR: i32 = -32700; + +#[derive(Debug)] +pub enum JsonRpcErrorReason { + ParseError, + InvalidRequest, + MethodNotFound, + InvalidParams, + InternalError, + /// -32000 to -32099 + ServerError(i32), +} + +impl std::fmt::Display for JsonRpcErrorReason { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + JsonRpcErrorReason::ParseError => write!(f, "Parse error"), + JsonRpcErrorReason::InvalidRequest => write!(f, "Invalid Request"), + JsonRpcErrorReason::MethodNotFound => write!(f, "Method not found"), + JsonRpcErrorReason::InvalidParams => write!(f, "Invalid params"), + JsonRpcErrorReason::InternalError => write!(f, "Internal error"), + JsonRpcErrorReason::ServerError(code) => write!(f, "Server error: {}", code), + } + } +} + +impl From for i32 { + fn from(reason: JsonRpcErrorReason) -> i32 { + match reason { + JsonRpcErrorReason::ParseError => PARSE_ERROR, + JsonRpcErrorReason::InvalidRequest => INVALID_REQUEST, + JsonRpcErrorReason::MethodNotFound => METHOD_NOT_FOUND, + JsonRpcErrorReason::InvalidParams => INVALID_PARAMS, + JsonRpcErrorReason::InternalError => INTERNAL_ERROR, + JsonRpcErrorReason::ServerError(code) => code, + } + } +} + +impl JsonRpcErrorReason { + fn new(code: i32) -> Self { + match code { + PARSE_ERROR => Self::ParseError, + INVALID_REQUEST => Self::InvalidRequest, + METHOD_NOT_FOUND => Self::MethodNotFound, + INVALID_PARAMS => Self::InvalidParams, + INTERNAL_ERROR => Self::InternalError, + other => Self::ServerError(other), + } + } +} + +#[derive(Debug, Error, Serialize, Deserialize)] +pub struct JsonRpcError { + code: i32, + message: String, + data: serde_json::Value, +} + +impl JsonRpcError { + pub fn new(code: JsonRpcErrorReason, message: String, data: serde_json::Value) -> Self { + Self { + code: code.into(), + message, + data, + } + } +} + +impl std::fmt::Display for JsonRpcError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}: {}", + JsonRpcErrorReason::new(self.code), + self.message + ) + } +} + +impl JsonRpcError { + pub fn error_reason(&self) -> JsonRpcErrorReason { + JsonRpcErrorReason::new(self.code) + } + + pub fn code(&self) -> i32 { + self.code + } +} diff --git a/src/json_rpc/mod.rs b/src/json_rpc/mod.rs new file mode 100644 index 0000000..c5e1cb1 --- /dev/null +++ b/src/json_rpc/mod.rs @@ -0,0 +1,191 @@ +// Copied from https://github.com/ralexstokes/axum-json-rpc since it needed minor modifications + +use axum::body::HttpBody; +use axum::extract::{FromRequest, FromRequestParts}; +use axum::http::Request; +use axum::response::{IntoResponse, Response}; +use axum::{BoxError, Json}; +use error::{JsonRpcError, JsonRpcErrorReason}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +pub mod error; + +/// Hack until [try_trait_v2](https://github.com/rust-lang/rust/issues/84277) is not stabilized +pub type JsonRpcResult = Result; + +#[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] +struct JsonRpcRequest { + id: i64, + jsonrpc: String, + method: String, + params: Option, +} + +/// Parses a JSON-RPC request, and returns the request ID, the method name, and the parameters. +/// If the request is invalid, returns an error. +/// ```rust +/// use axum_jrpc::{JsonRpcResult, JsonRpcExtractor, JsonRpcResponse}; +/// +/// fn router(req: JsonRpcExtractor) -> JsonRpcResult { +/// let req_id = req.get_request_id()?; +/// let method = req.method(); +/// match method { +/// "add" => { +/// let params: [i32;2] = req.parse_params()?; +/// return Ok(JsonRpcResponse::success(req_id, params[0] + params[1])) +/// } +/// m => Ok(req.method_not_found(m)) +/// } +/// } +/// ``` +#[derive(Debug)] +pub struct JsonRpcExtractor { + pub parsed: Value, + pub method: String, + pub id: i64, +} + +impl JsonRpcExtractor { + pub fn get_request_id(&self) -> i64 { + self.id + } + + pub fn parse_params(self) -> Result { + let value = serde_json::from_value(self.parsed); + match value { + Ok(v) => Ok(v), + Err(e) => { + let error = JsonRpcError::new( + JsonRpcErrorReason::InvalidParams, + e.to_string(), + Value::Null, + ); + Err(JsonRpcResponse::error(self.id, error)) + } + } + } + + pub fn method(&self) -> &str { + &self.method + } + + pub fn method_not_found(&self, method: &str) -> JsonRpcResponse { + let error = JsonRpcError::new( + JsonRpcErrorReason::MethodNotFound, + format!("Method `{}` not found", method), + Value::Null, + ); + JsonRpcResponse::error(self.id, error) + } +} + +#[async_trait::async_trait] +impl FromRequest for JsonRpcExtractor +where + S: Send + Sync, + B: Send + 'static, + B: HttpBody + Send, + B::Data: Send, + B::Error: Into, +{ + type Rejection = JsonRpcResponse; + + async fn from_request(req: Request, state: &S) -> Result { + let json = Json::from_request(req, state).await; + let parsed: JsonRpcRequest = match json { + Ok(a) => a.0, + Err(e) => { + return Err(JsonRpcResponse { + id: 0, + jsonrpc: "2.0".to_owned(), + result: JsonRpcAnswer::Error(JsonRpcError::new( + JsonRpcErrorReason::InvalidRequest, + e.to_string(), + Value::Null, + )), + }) + } + }; + if parsed.jsonrpc != "2.0" { + return Err(JsonRpcResponse { + id: parsed.id, + jsonrpc: "2.0".to_owned(), + result: JsonRpcAnswer::Error(JsonRpcError::new( + JsonRpcErrorReason::InvalidRequest, + "Invalid jsonrpc version".to_owned(), + Value::Null, + )), + }); + } + Ok(Self { + parsed: match parsed.params { + Some(p) => p, + None => Value::Null, + }, + method: parsed.method, + id: parsed.id, + }) + } +} + +#[derive(Serialize, Debug, Deserialize)] +/// A JSON-RPC response. +pub struct JsonRpcResponse { + jsonrpc: String, + pub result: JsonRpcAnswer, + /// The request ID. + id: i64, +} + +impl JsonRpcResponse { + /// Returns a response with the given result + /// Returns JsonRpcError if the `result` is invalid input for [`serde_json::to_value`] + pub fn success(id: i64, result: T) -> Self { + let result = match serde_json::to_value(result) { + Ok(v) => v, + Err(e) => { + let err = JsonRpcError::new( + JsonRpcErrorReason::InternalError, + e.to_string(), + Value::Null, + ); + return JsonRpcResponse { + id, + jsonrpc: "2.0".to_owned(), + result: JsonRpcAnswer::Error(err), + }; + } + }; + + JsonRpcResponse { + id, + jsonrpc: "2.0".to_owned(), + result: JsonRpcAnswer::Result(result), + } + } + + pub fn error(id: i64, error: JsonRpcError) -> Self { + JsonRpcResponse { + id, + jsonrpc: "2.0".to_owned(), + result: JsonRpcAnswer::Error(error), + } + } +} + +impl IntoResponse for JsonRpcResponse { + fn into_response(self) -> Response { + Json(self).into_response() + } +} + +#[derive(Serialize, Debug, Deserialize)] +#[serde(untagged)] +/// JsonRpc [response object](https://www.jsonrpc.org/specification#response_object) +pub enum JsonRpcAnswer { + Result(Value), + Error(JsonRpcError), +} diff --git a/src/main.rs b/src/main.rs index cf69770..1c543fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use web::auth::SessionState; mod config_manager; mod definitions; +mod json_rpc; mod state; mod web; diff --git a/src/web/rpc.rs b/src/web/rpc.rs index daae823..cab913a 100644 --- a/src/web/rpc.rs +++ b/src/web/rpc.rs @@ -1,3 +1,4 @@ +use super::super::json_rpc::{JsonRpcExtractor, JsonRpcResponse, JsonRpcResult}; use super::super::AppState; use axum::routing::post; use axum::{Json, Router}; @@ -20,7 +21,22 @@ pub fn routes() -> Router { async fn api_handler( State(state): State, session: Extension, -) -> impl IntoResponse { + req: JsonRpcExtractor, +) -> JsonRpcResult { info!("api hit! user: {:?}", session.username); - StatusCode::NOT_IMPLEMENTED + let req_id = req.get_request_id(); + let method = req.method(); + let response = match method { + "add" => { + let params: [i32; 2] = req.parse_params()?; + JsonRpcResponse::success(req_id, params[0] + params[1]) + } + "System.GetUsers" => JsonRpcResponse::success( + req_id, + state.config_manager.get_pending_config().system.users, + ), + m => req.method_not_found(m), + }; + + Ok(response) } From 6b75341d03da16612471f5675b4a9a6ae53af62d Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 27 Oct 2023 00:22:07 +0200 Subject: [PATCH 26/29] WIP use jsonrpsee rpcmodule --- Cargo.lock | 215 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/web/rpc.rs | 86 ++++++++++++++++---- 3 files changed, 285 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87372cd..16b72f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "async-trait" version = "0.1.74" @@ -107,6 +113,21 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -244,6 +265,20 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -251,6 +286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -259,6 +295,12 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + [[package]] name = "futures-macro" version = "0.3.28" @@ -270,6 +312,12 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + [[package]] name = "futures-task" version = "0.3.28" @@ -282,9 +330,13 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -317,6 +369,31 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hermit-abi" version = "0.3.3" @@ -383,6 +460,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", @@ -423,6 +501,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "ipnet" version = "2.8.0" @@ -438,6 +526,78 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jsonrpsee" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affdc52f7596ccb2d7645231fc6163bb314630c989b64998f3699a28b4d5d4dc" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-server", + "jsonrpsee-types", + "tokio", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2327ba8df2fdbd5e897e2b5ed25ce7f299d345b9736b6828814c3dbd1fd47b" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "futures-util", + "hyper", + "jsonrpsee-types", + "parking_lot", + "rand", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c39a00449c9ef3f50b84fc00fc4acba20ef8f559f07902244abf4c15c5ab9c" +dependencies = [ + "futures-util", + "http", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be0be325642e850ed0bdff426674d2e66b2b7117c9be23a7caef68a2902b7d9" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -538,6 +698,7 @@ dependencies = [ "axum", "custom_error", "ipnet", + "jsonrpsee", "macaddr", "pwhash", "serde", @@ -797,12 +958,24 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustversion" version = "1.0.14" @@ -953,6 +1126,22 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand", + "sha-1", +] + [[package]] name = "subtle" version = "2.4.1" @@ -1091,6 +1280,32 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 4a65bef..43ca43e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ async-trait = "0.1.74" axum = "0.6.20" custom_error = "1.9.2" ipnet = { version = "2.8.0", features = ["serde"] } +jsonrpsee = { version = "0.20.3", features = ["server"] } macaddr = { version = "1.0.1", features = ["serde"] } pwhash = "1.0.0" serde = { version = "1.0.189", features = ["derive"] } diff --git a/src/web/rpc.rs b/src/web/rpc.rs index cab913a..3b776e5 100644 --- a/src/web/rpc.rs +++ b/src/web/rpc.rs @@ -1,7 +1,11 @@ -use super::super::json_rpc::{JsonRpcExtractor, JsonRpcResponse, JsonRpcResult}; +use std::collections::HashMap; + +use super::super::definitions::network::StaticRoute; +use super::super::definitions::system::User; use super::super::AppState; use axum::routing::post; use axum::{Json, Router}; +use jsonrpsee::types::Params; use tower_cookies::{Cookie, Cookies}; use axum::{ @@ -12,8 +16,29 @@ use axum::{ response::{IntoResponse, Response}, }; +use jsonrpsee::server::{RpcModule, Server}; + use tracing::info; +use custom_error::custom_error; + +custom_error! { ApiError + BadRequest = "Bad Request Parameters", +} + +struct RpcRequest<'a> { + id: i64, + params: Params<'a>, + jsonrpc: String, + method: String, +} + +struct RpcResponse<'a> { + id: i64, + payload: Params<'a>, + jsonrpc: String, +} + pub fn routes() -> Router { Router::new().route("/api", post(api_handler)) } @@ -21,22 +46,49 @@ pub fn routes() -> Router { async fn api_handler( State(state): State, session: Extension, - req: JsonRpcExtractor, -) -> JsonRpcResult { + Json(rpc_request): Json>, +) -> impl IntoResponse { info!("api hit! user: {:?}", session.username); - let req_id = req.get_request_id(); - let method = req.method(); - let response = match method { - "add" => { - let params: [i32; 2] = req.parse_params()?; - JsonRpcResponse::success(req_id, params[0] + params[1]) - } - "System.GetUsers" => JsonRpcResponse::success( - req_id, - state.config_manager.get_pending_config().system.users, - ), - m => req.method_not_found(m), - }; + let module = RpcModule::new(state); + module + .register_method("say_hello", |_, _| { + println!("say_hello method called!"); + "Hello there!!" + }) + .unwrap(); - Ok(response) + module + .register_method("System.GetUsers", get_users) + .unwrap(); + module + .register_method("Network.GetStaticRoutes", get_static_routes) + .unwrap(); + + let res = module.call(&rpc_request.method, rpc_request.params).await; + + match res { + Ok(res) => RpcResponse { + id: rpc_request.id, + jsonrpc: rpc_request.jsonrpc, + payload: res, + }, + // TODO make Error Response + Err(err) => RpcResponse { + id: rpc_request.id, + jsonrpc: rpc_request.jsonrpc, + payload: res, + }, + } +} + +fn get_users(_: Params, state: &AppState) -> Result, String> { + Ok(state.config_manager.get_pending_config().system.users) +} + +fn get_static_routes(_: Params, state: &AppState) -> Result, String> { + Ok(state + .config_manager + .get_pending_config() + .network + .static_routes) } From 0a603b6642ae5e3e56cd08394198fd79fbdda1f4 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 28 Oct 2023 14:14:48 +0200 Subject: [PATCH 27/29] remove json_rpc modified crate --- src/json_rpc/error.rs | 97 --------------------- src/json_rpc/mod.rs | 191 ------------------------------------------ src/main.rs | 1 - 3 files changed, 289 deletions(-) delete mode 100644 src/json_rpc/error.rs delete mode 100644 src/json_rpc/mod.rs diff --git a/src/json_rpc/error.rs b/src/json_rpc/error.rs deleted file mode 100644 index 21fb2d8..0000000 --- a/src/json_rpc/error.rs +++ /dev/null @@ -1,97 +0,0 @@ -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -/// Constants for [error object](https://www.jsonrpc.org/specification#error_object) -pub const INVALID_REQUEST: i32 = -32600; -pub const METHOD_NOT_FOUND: i32 = -32601; -pub const INVALID_PARAMS: i32 = -32602; -pub const INTERNAL_ERROR: i32 = -32603; -pub const PARSE_ERROR: i32 = -32700; - -#[derive(Debug)] -pub enum JsonRpcErrorReason { - ParseError, - InvalidRequest, - MethodNotFound, - InvalidParams, - InternalError, - /// -32000 to -32099 - ServerError(i32), -} - -impl std::fmt::Display for JsonRpcErrorReason { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - JsonRpcErrorReason::ParseError => write!(f, "Parse error"), - JsonRpcErrorReason::InvalidRequest => write!(f, "Invalid Request"), - JsonRpcErrorReason::MethodNotFound => write!(f, "Method not found"), - JsonRpcErrorReason::InvalidParams => write!(f, "Invalid params"), - JsonRpcErrorReason::InternalError => write!(f, "Internal error"), - JsonRpcErrorReason::ServerError(code) => write!(f, "Server error: {}", code), - } - } -} - -impl From for i32 { - fn from(reason: JsonRpcErrorReason) -> i32 { - match reason { - JsonRpcErrorReason::ParseError => PARSE_ERROR, - JsonRpcErrorReason::InvalidRequest => INVALID_REQUEST, - JsonRpcErrorReason::MethodNotFound => METHOD_NOT_FOUND, - JsonRpcErrorReason::InvalidParams => INVALID_PARAMS, - JsonRpcErrorReason::InternalError => INTERNAL_ERROR, - JsonRpcErrorReason::ServerError(code) => code, - } - } -} - -impl JsonRpcErrorReason { - fn new(code: i32) -> Self { - match code { - PARSE_ERROR => Self::ParseError, - INVALID_REQUEST => Self::InvalidRequest, - METHOD_NOT_FOUND => Self::MethodNotFound, - INVALID_PARAMS => Self::InvalidParams, - INTERNAL_ERROR => Self::InternalError, - other => Self::ServerError(other), - } - } -} - -#[derive(Debug, Error, Serialize, Deserialize)] -pub struct JsonRpcError { - code: i32, - message: String, - data: serde_json::Value, -} - -impl JsonRpcError { - pub fn new(code: JsonRpcErrorReason, message: String, data: serde_json::Value) -> Self { - Self { - code: code.into(), - message, - data, - } - } -} - -impl std::fmt::Display for JsonRpcError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}: {}", - JsonRpcErrorReason::new(self.code), - self.message - ) - } -} - -impl JsonRpcError { - pub fn error_reason(&self) -> JsonRpcErrorReason { - JsonRpcErrorReason::new(self.code) - } - - pub fn code(&self) -> i32 { - self.code - } -} diff --git a/src/json_rpc/mod.rs b/src/json_rpc/mod.rs deleted file mode 100644 index c5e1cb1..0000000 --- a/src/json_rpc/mod.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copied from https://github.com/ralexstokes/axum-json-rpc since it needed minor modifications - -use axum::body::HttpBody; -use axum::extract::{FromRequest, FromRequestParts}; -use axum::http::Request; -use axum::response::{IntoResponse, Response}; -use axum::{BoxError, Json}; -use error::{JsonRpcError, JsonRpcErrorReason}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -pub mod error; - -/// Hack until [try_trait_v2](https://github.com/rust-lang/rust/issues/84277) is not stabilized -pub type JsonRpcResult = Result; - -#[derive(Deserialize, Debug)] -#[serde(deny_unknown_fields)] -struct JsonRpcRequest { - id: i64, - jsonrpc: String, - method: String, - params: Option, -} - -/// Parses a JSON-RPC request, and returns the request ID, the method name, and the parameters. -/// If the request is invalid, returns an error. -/// ```rust -/// use axum_jrpc::{JsonRpcResult, JsonRpcExtractor, JsonRpcResponse}; -/// -/// fn router(req: JsonRpcExtractor) -> JsonRpcResult { -/// let req_id = req.get_request_id()?; -/// let method = req.method(); -/// match method { -/// "add" => { -/// let params: [i32;2] = req.parse_params()?; -/// return Ok(JsonRpcResponse::success(req_id, params[0] + params[1])) -/// } -/// m => Ok(req.method_not_found(m)) -/// } -/// } -/// ``` -#[derive(Debug)] -pub struct JsonRpcExtractor { - pub parsed: Value, - pub method: String, - pub id: i64, -} - -impl JsonRpcExtractor { - pub fn get_request_id(&self) -> i64 { - self.id - } - - pub fn parse_params(self) -> Result { - let value = serde_json::from_value(self.parsed); - match value { - Ok(v) => Ok(v), - Err(e) => { - let error = JsonRpcError::new( - JsonRpcErrorReason::InvalidParams, - e.to_string(), - Value::Null, - ); - Err(JsonRpcResponse::error(self.id, error)) - } - } - } - - pub fn method(&self) -> &str { - &self.method - } - - pub fn method_not_found(&self, method: &str) -> JsonRpcResponse { - let error = JsonRpcError::new( - JsonRpcErrorReason::MethodNotFound, - format!("Method `{}` not found", method), - Value::Null, - ); - JsonRpcResponse::error(self.id, error) - } -} - -#[async_trait::async_trait] -impl FromRequest for JsonRpcExtractor -where - S: Send + Sync, - B: Send + 'static, - B: HttpBody + Send, - B::Data: Send, - B::Error: Into, -{ - type Rejection = JsonRpcResponse; - - async fn from_request(req: Request, state: &S) -> Result { - let json = Json::from_request(req, state).await; - let parsed: JsonRpcRequest = match json { - Ok(a) => a.0, - Err(e) => { - return Err(JsonRpcResponse { - id: 0, - jsonrpc: "2.0".to_owned(), - result: JsonRpcAnswer::Error(JsonRpcError::new( - JsonRpcErrorReason::InvalidRequest, - e.to_string(), - Value::Null, - )), - }) - } - }; - if parsed.jsonrpc != "2.0" { - return Err(JsonRpcResponse { - id: parsed.id, - jsonrpc: "2.0".to_owned(), - result: JsonRpcAnswer::Error(JsonRpcError::new( - JsonRpcErrorReason::InvalidRequest, - "Invalid jsonrpc version".to_owned(), - Value::Null, - )), - }); - } - Ok(Self { - parsed: match parsed.params { - Some(p) => p, - None => Value::Null, - }, - method: parsed.method, - id: parsed.id, - }) - } -} - -#[derive(Serialize, Debug, Deserialize)] -/// A JSON-RPC response. -pub struct JsonRpcResponse { - jsonrpc: String, - pub result: JsonRpcAnswer, - /// The request ID. - id: i64, -} - -impl JsonRpcResponse { - /// Returns a response with the given result - /// Returns JsonRpcError if the `result` is invalid input for [`serde_json::to_value`] - pub fn success(id: i64, result: T) -> Self { - let result = match serde_json::to_value(result) { - Ok(v) => v, - Err(e) => { - let err = JsonRpcError::new( - JsonRpcErrorReason::InternalError, - e.to_string(), - Value::Null, - ); - return JsonRpcResponse { - id, - jsonrpc: "2.0".to_owned(), - result: JsonRpcAnswer::Error(err), - }; - } - }; - - JsonRpcResponse { - id, - jsonrpc: "2.0".to_owned(), - result: JsonRpcAnswer::Result(result), - } - } - - pub fn error(id: i64, error: JsonRpcError) -> Self { - JsonRpcResponse { - id, - jsonrpc: "2.0".to_owned(), - result: JsonRpcAnswer::Error(error), - } - } -} - -impl IntoResponse for JsonRpcResponse { - fn into_response(self) -> Response { - Json(self).into_response() - } -} - -#[derive(Serialize, Debug, Deserialize)] -#[serde(untagged)] -/// JsonRpc [response object](https://www.jsonrpc.org/specification#response_object) -pub enum JsonRpcAnswer { - Result(Value), - Error(JsonRpcError), -} diff --git a/src/main.rs b/src/main.rs index 1c543fd..cf69770 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,6 @@ use web::auth::SessionState; mod config_manager; mod definitions; -mod json_rpc; mod state; mod web; From 42cc14cb148e7f5605469b7d38fa575fade4ffcc Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 28 Oct 2023 15:44:12 +0200 Subject: [PATCH 28/29] Get JsonRPC Working --- src/api/mod.rs | 48 +++++++++++++++++ src/api/network.rs | 13 +++++ src/api/system.rs | 10 ++++ src/main.rs | 15 ++++-- src/state.rs | 9 ++++ src/web/rpc.rs | 131 ++++++++++++++++++++++++--------------------- 6 files changed, 160 insertions(+), 66 deletions(-) create mode 100644 src/api/mod.rs create mode 100644 src/api/network.rs create mode 100644 src/api/system.rs diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 0000000..87f4de3 --- /dev/null +++ b/src/api/mod.rs @@ -0,0 +1,48 @@ +mod network; +mod system; + +use crate::state::RpcState; +use jsonrpsee::{ + types::{error::ErrorCode, ErrorObject}, + RpcModule, +}; + +use custom_error::custom_error; +use tracing::info; + +custom_error! { pub ApiError + InvalidParams = "Invalid Parameters", + Leet = "1337", +} + +impl Into> for ApiError { + fn into(self) -> ErrorObject<'static> { + match self { + Self::InvalidParams => ErrorCode::InvalidParams, + Self::Leet => ErrorCode::ServerError(1337), + _ => ErrorCode::InternalError, + } + .into() + } +} + +pub fn new_rpc_module(state: RpcState) -> RpcModule { + let mut module = RpcModule::new(state); + + module + .register_method("ping", |_, _| { + info!("ping called"); + "pong" + }) + .unwrap(); + + module + .register_method("System.GetUsers", system::get_users) + .unwrap(); + + module + .register_method("Network.GetStaticRoutes", network::get_static_routes) + .unwrap(); + + module +} diff --git a/src/api/network.rs b/src/api/network.rs new file mode 100644 index 0000000..bf92118 --- /dev/null +++ b/src/api/network.rs @@ -0,0 +1,13 @@ +use jsonrpsee::types::Params; + +use crate::{definitions::network::StaticRoute, state::RpcState}; + +use super::ApiError; + +pub fn get_static_routes(_: Params, state: &RpcState) -> Result, ApiError> { + Ok(state + .config_manager + .get_pending_config() + .network + .static_routes) +} diff --git a/src/api/system.rs b/src/api/system.rs new file mode 100644 index 0000000..01ec35e --- /dev/null +++ b/src/api/system.rs @@ -0,0 +1,10 @@ +use std::collections::HashMap; + +use crate::{definitions::system::User, state::RpcState}; +use jsonrpsee::types::Params; + +use super::ApiError; + +pub fn get_users(_: Params, state: &RpcState) -> Result, ApiError> { + Ok(state.config_manager.get_pending_config().system.users) +} diff --git a/src/main.rs b/src/main.rs index cf69770..9fda6f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use std::{ sync::{Arc, RwLock}, }; +use crate::state::RpcState; use axum::{middleware, Router}; use config_manager::ConfigManager; use state::AppState; @@ -15,6 +16,7 @@ use tracing::info; use tracing_subscriber; use web::auth::SessionState; +mod api; mod config_manager; mod definitions; mod state; @@ -41,12 +43,17 @@ async fn main() { // TODO Check Config Manager Setup Error let config_manager = ConfigManager::new().unwrap(); + let session_state = SessionState { + sessions: Arc::new(RwLock::new(HashMap::new())), + }; let app_state = AppState { - config_manager, - session_state: SessionState { - sessions: Arc::new(RwLock::new(HashMap::new())), - }, + config_manager: config_manager.clone(), + session_state: session_state.clone(), + rpc_module: api::new_rpc_module(RpcState { + config_manager, + session_state, + }), }; // Note: The Router Works Bottom Up, So the auth middleware will only applies to everything above it. diff --git a/src/state.rs b/src/state.rs index 26f5551..fb63902 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,3 +1,5 @@ +use jsonrpsee::RpcModule; + use super::config_manager::ConfigManager; use super::web::auth::SessionState; @@ -5,4 +7,11 @@ use super::web::auth::SessionState; pub struct AppState { pub config_manager: ConfigManager, pub session_state: SessionState, + pub rpc_module: RpcModule, +} + +#[derive(Clone)] +pub struct RpcState { + pub config_manager: ConfigManager, + pub session_state: SessionState, } diff --git a/src/web/rpc.rs b/src/web/rpc.rs index 3b776e5..d980b98 100644 --- a/src/web/rpc.rs +++ b/src/web/rpc.rs @@ -1,42 +1,60 @@ -use std::collections::HashMap; - -use super::super::definitions::network::StaticRoute; -use super::super::definitions::system::User; -use super::super::AppState; +use crate::AppState; use axum::routing::post; use axum::{Json, Router}; -use jsonrpsee::types::Params; -use tower_cookies::{Cookie, Cookies}; +use jsonrpsee::core::traits::ToRpcParams; +use jsonrpsee::core::Error; +use serde::{Deserialize, Serialize}; +use serde_json::value::RawValue; -use axum::{ - extract::Extension, - extract::State, - http::{Request, StatusCode}, - middleware::{self, Next}, - response::{IntoResponse, Response}, -}; - -use jsonrpsee::server::{RpcModule, Server}; +use axum::{extract::Extension, extract::State, response::IntoResponse}; use tracing::info; -use custom_error::custom_error; - -custom_error! { ApiError - BadRequest = "Bad Request Parameters", +// TODO fix this "workaround" +struct ParamConverter { + params: Option>, } -struct RpcRequest<'a> { +impl ToRpcParams for ParamConverter { + fn to_rpc_params(self) -> Result>, Error> { + let s = String::from_utf8(serde_json::to_vec(&self.params)?); + match s { + Ok(s) => { + return RawValue::from_string(s) + .map(Some) + .map_err(Error::ParseError) + } + // TODO make this a Parse error wrapping Utf8Error + Err(err) => return Err(Error::AlreadyStopped), + } + } +} + +#[derive(Deserialize)] +struct RpcRequest { id: i64, - params: Params<'a>, + params: Option>, jsonrpc: String, method: String, } -struct RpcResponse<'a> { +#[derive(Clone, Deserialize, Serialize)] +struct RpcResponse { id: i64, - payload: Params<'a>, jsonrpc: String, + #[serde(skip_serializing_if = "Option::is_none")] + result: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + error: Option, +} + +#[derive(Clone, Deserialize, Serialize)] + +struct RpcErrorObject { + code: i64, + message: String, + #[serde(skip_serializing_if = "Option::is_none")] + data: Option>, } pub fn routes() -> Router { @@ -46,49 +64,38 @@ pub fn routes() -> Router { async fn api_handler( State(state): State, session: Extension, - Json(rpc_request): Json>, + body: String, ) -> impl IntoResponse { info!("api hit! user: {:?}", session.username); - let module = RpcModule::new(state); - module - .register_method("say_hello", |_, _| { - println!("say_hello method called!"); - "Hello there!!" - }) - .unwrap(); - module - .register_method("System.GetUsers", get_users) - .unwrap(); - module - .register_method("Network.GetStaticRoutes", get_static_routes) - .unwrap(); + // TODO handle Parse Error + let req: RpcRequest = serde_json::from_str(&body).unwrap(); - let res = module.call(&rpc_request.method, rpc_request.params).await; + // TODO check version + + let params = ParamConverter { params: req.params }; + + // TODO check Permissions for method here? + + let res: Result>, Error> = + state.rpc_module.call(&req.method, params).await; match res { - Ok(res) => RpcResponse { - id: rpc_request.id, - jsonrpc: rpc_request.jsonrpc, - payload: res, - }, - // TODO make Error Response - Err(err) => RpcResponse { - id: rpc_request.id, - jsonrpc: rpc_request.jsonrpc, - payload: res, - }, + Ok(res) => Json(RpcResponse { + id: req.id, + jsonrpc: req.jsonrpc, + result: res, + error: None, + }), + Err(err) => Json(RpcResponse { + id: req.id, + jsonrpc: req.jsonrpc, + result: None, + error: Some(RpcErrorObject { + code: 10, + message: err.to_string(), + data: None, + }), + }), } } - -fn get_users(_: Params, state: &AppState) -> Result, String> { - Ok(state.config_manager.get_pending_config().system.users) -} - -fn get_static_routes(_: Params, state: &AppState) -> Result, String> { - Ok(state - .config_manager - .get_pending_config() - .network - .static_routes) -} From 2281803fa0ceae4dfba14875d6e66fc3c575dedb Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Sat, 28 Oct 2023 18:33:47 +0200 Subject: [PATCH 29/29] rename rpc methods --- src/api/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index 87f4de3..585ab03 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -37,11 +37,11 @@ pub fn new_rpc_module(state: RpcState) -> RpcModule { .unwrap(); module - .register_method("System.GetUsers", system::get_users) + .register_method("system.get_users", system::get_users) .unwrap(); module - .register_method("Network.GetStaticRoutes", network::get_static_routes) + .register_method("network.get_static_routes", network::get_static_routes) .unwrap(); module