summaryrefslogtreecommitdiff
path: root/webhook/src/handler/tests
diff options
context:
space:
mode:
authorMatthieu <matthieu@developershouse.xyz>2021-10-16 09:33:56 +0400
committerMatthieu <matthieu@developershouse.xyz>2021-10-16 09:33:56 +0400
commitb94b0552f81e667bec31352901bbc8c76f1b4216 (patch)
treeccc73abd12875502a954eaf10d080e02b5eb42ac /webhook/src/handler/tests
parentb8c904a2ccef5b7389356b858a3fd4ffbbfb1ae6 (diff)
parentd2aa675b39361eccb1f5e74b08bc6428f564daa3 (diff)
merge branch management
Diffstat (limited to 'webhook/src/handler/tests')
-rw-r--r--webhook/src/handler/tests/handler.rs0
-rw-r--r--webhook/src/handler/tests/handler_integration.rs234
-rw-r--r--webhook/src/handler/tests/mod.rs4
-rw-r--r--webhook/src/handler/tests/signature.rs30
-rw-r--r--webhook/src/handler/tests/utils.rs46
5 files changed, 314 insertions, 0 deletions
diff --git a/webhook/src/handler/tests/handler.rs b/webhook/src/handler/tests/handler.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webhook/src/handler/tests/handler.rs
diff --git a/webhook/src/handler/tests/handler_integration.rs b/webhook/src/handler/tests/handler_integration.rs
new file mode 100644
index 0000000..fd0d67f
--- /dev/null
+++ b/webhook/src/handler/tests/handler_integration.rs
@@ -0,0 +1,234 @@
+use std::time::Duration;
+
+use crate::{
+ config::Config,
+ handler::tests::utils::{generate_keypair, sign_message},
+ start,
+};
+use common::{config::test_init, nats_crate::Connection, testcontainers::{Image, images::generic::WaitFor}};
+use common::{
+ config::Settings,
+ log::info,
+ testcontainers::{clients::Cli, images::generic::GenericImage, Container, Docker},
+};
+use hyper::{Body, Method, Request};
+use lazy_static::{__Deref, lazy_static};
+use serde_json::json;
+
+#[cfg(all(unix, target_arch = "x86_64"))]
+const fn nats_image<'a>() -> &'a str {
+ return "amd64/nats";
+}
+
+#[cfg(all(unix, target_arch = "aarch64"))]
+const fn nats_image<'a>() -> &'a str {
+ return "arm64v8/nats";
+}
+
+#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
+const fn nats_image<'a>() -> &'a str {
+ return "winamd64/nats";
+}
+
+lazy_static! {
+ static ref DOCKER: Cli = Cli::default();
+
+ static ref NATS_CONTAINER: Container<'static, Cli, GenericImage> = {
+ test_init();
+
+ let image: GenericImage = GenericImage::new(nats_image())
+ .with_wait_for(WaitFor::message_on_stderr("Server is ready"));
+
+ let container = DOCKER.run(image);
+ container.start();
+ container.image().wait_until_ready(&container);
+ container.get_host_port(4222).unwrap();
+ container
+ };
+
+
+ static ref KEYPAIR: (String, [u8; 64]) = {
+ generate_keypair()
+ };
+
+ static ref SETTINGS: Settings<Config> = {
+ let port = NATS_CONTAINER.get_host_port(4222).unwrap();
+ common::config::Settings {
+ config: crate::config::Config {
+ server: crate::config::ServerSettings {
+ port: 5003,
+ address: "0.0.0.0".to_string(),
+ },
+ discord: crate::config::Discord {
+ public_key: KEYPAIR.0.clone(),
+ client_id: 0,
+ },
+ },
+ redis: common::redis::RedisConfiguration {
+ url: "".to_string(),
+ },
+ monitoring: common::monitoring::MonitoringConfiguration {
+ enabled: false,
+ address: None,
+ port: None,
+ },
+ nats: common::nats::NatsConfiguration {
+ client_cert: None,
+ root_cert: None,
+ jetstream_api_prefix: None,
+ max_reconnects: None,
+ reconnect_buffer_size: None,
+ tls: None,
+ client_name: None,
+ tls_required: None,
+ host: format!("localhost:{}", port),
+ },
+ }
+ };
+
+ static ref TASK: () = {
+ std::thread::spawn(|| {
+ let r = tokio::runtime::Runtime::new().unwrap();
+ r.spawn(async { start(SETTINGS.clone()).await });
+ loop {}
+ });
+ std::thread::sleep(Duration::from_secs(1));
+ };
+}
+
+#[tokio::test]
+async fn respond_to_pings() {
+ let _ = NATS_CONTAINER.deref();
+ let _ = TASK.deref();
+ let ping = json!({ "type": 1 }).to_string();
+ let timestamp = "my datetime :)";
+ let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
+ let signature = sign_message(signature_data, KEYPAIR.1);
+
+ let req = Request::builder()
+ .method(Method::POST)
+ .uri("http://localhost:5003/")
+ .header("X-Signature-Ed25519", signature)
+ .header("X-Signature-Timestamp", timestamp)
+ .body(Body::from(ping.clone()))
+ .expect("request builder");
+ let client = hyper::client::Client::new();
+ let result = client.request(req).await.unwrap();
+
+ assert!(result.status() == 200);
+}
+
+#[tokio::test]
+async fn deny_invalid_signatures() {
+ let _ = NATS_CONTAINER.deref();
+ let _ = TASK.deref();
+ let ping = json!({ "type": 1 }).to_string();
+ let timestamp = "my datetime :)";
+
+ let req = Request::builder()
+ .method(Method::POST)
+ .uri("http://localhost:5003/")
+ .header("X-Signature-Ed25519", "inva&lid signature :)")
+ .header("X-Signature-Timestamp", timestamp)
+ .body(Body::from(ping.clone()))
+ .expect("request builder");
+ let client = hyper::client::Client::new();
+ let result = client.request(req).await.unwrap();
+ assert!(result.status() == 401);
+}
+
+#[tokio::test]
+async fn response_500_when_no_nats_response() {
+ let _ = NATS_CONTAINER.deref();
+ let _ = TASK.deref();
+ let ping = json!({ "type": 0 }).to_string();
+ let timestamp = "my datetime :)";
+ let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
+ let signature = sign_message(signature_data, KEYPAIR.1);
+
+ // we must timeout
+ let req = Request::builder()
+ .method(Method::POST)
+ .uri("http://localhost:5003/")
+ .header("X-Signature-Ed25519", signature)
+ .header("X-Signature-Timestamp", timestamp)
+ .body(Body::from(ping.clone()))
+ .expect("request builder");
+
+ let client = hyper::client::Client::new();
+ let result = client.request(req).await.unwrap();
+ assert!(result.status() == 500);
+}
+
+#[tokio::test]
+async fn respond_from_nats_response() {
+ let _ = NATS_CONTAINER.deref();
+ let _ = TASK.deref();
+ let nats: Connection = SETTINGS.clone().nats.into();
+ let sub = nats.subscribe("nova.cache.dispatch.interaction").unwrap();
+ let ping = json!({ "type": 0 }).to_string();
+ let timestamp = "my datetime :)";
+ let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
+ let signature = sign_message(signature_data, KEYPAIR.1);
+
+ sub.with_handler(move |msg| {
+ info!("Received {}", &msg);
+ msg.respond("ok :)").unwrap();
+ Ok(())
+ });
+
+ let req = Request::builder()
+ .method(Method::POST)
+ .uri("http://localhost:5003/")
+ .header("X-Signature-Ed25519", signature)
+ .header("X-Signature-Timestamp", timestamp)
+ .body(Body::from(ping.clone()))
+ .expect("request builder");
+ let client = hyper::client::Client::new();
+ let result = client.request(req).await.unwrap();
+ assert!(result.status() == 200);
+}
+
+#[tokio::test]
+async fn response_400_when_invalid_json_body() {
+ let _ = NATS_CONTAINER.deref();
+ let _ = TASK.deref();
+ let ping = "{".to_string();
+ let timestamp = "my datetime :)";
+ let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat();
+ let signature = sign_message(signature_data, KEYPAIR.1);
+
+ let req = Request::builder()
+ .method(Method::POST)
+ .uri("http://localhost:5003/")
+ .header("X-Signature-Ed25519", signature)
+ .header("X-Signature-Timestamp", timestamp)
+ .body(Body::from(ping.clone()))
+ .expect("request builder");
+ let client = hyper::client::Client::new();
+ let result = client.request(req).await.unwrap();
+ assert!(result.status() == 400);
+}
+
+#[tokio::test]
+async fn response_400_when_invalid_utf8_body() {
+ let _ = NATS_CONTAINER.deref();
+ let _ = TASK.deref();
+ // invalid 2 octet sequence
+ let ping = vec![0xc3, 0x28];
+
+ let timestamp = "my datetime :)";
+ let signature_data = [timestamp.as_bytes().to_vec(), ping.to_vec()].concat();
+ let signature = sign_message(signature_data, KEYPAIR.1);
+
+ let req = Request::builder()
+ .method(Method::POST)
+ .uri("http://localhost:5003/")
+ .header("X-Signature-Ed25519", signature)
+ .header("X-Signature-Timestamp", timestamp)
+ .body(Body::from(ping.clone()))
+ .expect("request builder");
+ let client = hyper::client::Client::new();
+ let result = client.request(req).await.unwrap();
+ assert!(result.status() == 400);
+}
diff --git a/webhook/src/handler/tests/mod.rs b/webhook/src/handler/tests/mod.rs
new file mode 100644
index 0000000..589ad52
--- /dev/null
+++ b/webhook/src/handler/tests/mod.rs
@@ -0,0 +1,4 @@
+pub mod handler_integration;
+pub mod signature;
+pub mod utils;
+pub mod handler;
diff --git a/webhook/src/handler/tests/signature.rs b/webhook/src/handler/tests/signature.rs
new file mode 100644
index 0000000..475e446
--- /dev/null
+++ b/webhook/src/handler/tests/signature.rs
@@ -0,0 +1,30 @@
+use crate::handler::signature::validate_signature;
+
+
+#[test]
+fn validate_signature_test() {
+ let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
+ let public_key = "eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f";
+ let content = "message de test incroyable".as_bytes().to_vec();
+ assert!(validate_signature(public_key, &content, signature))
+}
+
+#[test]
+fn validate_signature_reverse_test() {
+ let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
+ let public_key = "c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044";
+ let content = "ceci est un test qui ne fonctionnera pas!"
+ .as_bytes()
+ .to_vec();
+ assert!(!validate_signature(public_key, &content, signature))
+}
+
+#[test]
+fn invalid_hex() {
+ let signature = "zzz";
+ let public_key = "zzz";
+ let content = "ceci est un test qui ne fonctionnera pas!"
+ .as_bytes()
+ .to_vec();
+ assert!(!validate_signature(public_key, &content, signature))
+} \ No newline at end of file
diff --git a/webhook/src/handler/tests/utils.rs b/webhook/src/handler/tests/utils.rs
new file mode 100644
index 0000000..f8cdac2
--- /dev/null
+++ b/webhook/src/handler/tests/utils.rs
@@ -0,0 +1,46 @@
+pub fn generate_keypair() -> (
+ String,
+ [u8; libsodium_sys::crypto_sign_ed25519_SECRETKEYBYTES as usize],
+) {
+ use libsodium_sys::crypto_sign_ed25519_keypair;
+ let pk_s: String;
+
+ let mut pk = [0; libsodium_sys::crypto_sign_ed25519_PUBLICKEYBYTES as usize];
+ let mut sk = [0; libsodium_sys::crypto_sign_ed25519_SECRETKEYBYTES as usize];
+
+ let pk_p = pk.as_mut_ptr();
+ let sk_p = sk.as_mut_ptr();
+
+ // generate keypair
+ unsafe {
+ if crypto_sign_ed25519_keypair(pk_p, sk_p) < 0 {
+ panic!("keypair generation failed!");
+ }
+ };
+
+ pk_s = hex::encode(pk);
+ return (pk_s, sk);
+}
+
+pub fn sign_message(
+ msg: Vec<u8>,
+ sk: [u8; libsodium_sys::crypto_sign_ed25519_SECRETKEYBYTES as usize],
+) -> String {
+ use libc::c_ulonglong;
+ use libsodium_sys::crypto_sign_ed25519_detached;
+
+ let len = msg.len();
+ let mut signature_len: c_ulonglong = 0;
+ let mut str = [0; 64];
+ unsafe {
+ crypto_sign_ed25519_detached(
+ str.as_mut_ptr(),
+ &mut signature_len,
+ msg.as_ptr(),
+ len as u64,
+ sk.as_ptr(),
+ );
+ };
+
+ return hex::encode(str);
+} \ No newline at end of file