diff options
| author | Matthieu <matthieu@developershouse.xyz> | 2021-10-16 09:33:56 +0400 |
|---|---|---|
| committer | Matthieu <matthieu@developershouse.xyz> | 2021-10-16 09:33:56 +0400 |
| commit | b94b0552f81e667bec31352901bbc8c76f1b4216 (patch) | |
| tree | ccc73abd12875502a954eaf10d080e02b5eb42ac /webhook/src/handler/tests | |
| parent | b8c904a2ccef5b7389356b858a3fd4ffbbfb1ae6 (diff) | |
| parent | d2aa675b39361eccb1f5e74b08bc6428f564daa3 (diff) | |
merge branch management
Diffstat (limited to 'webhook/src/handler/tests')
| -rw-r--r-- | webhook/src/handler/tests/handler.rs | 0 | ||||
| -rw-r--r-- | webhook/src/handler/tests/handler_integration.rs | 234 | ||||
| -rw-r--r-- | webhook/src/handler/tests/mod.rs | 4 | ||||
| -rw-r--r-- | webhook/src/handler/tests/signature.rs | 30 | ||||
| -rw-r--r-- | webhook/src/handler/tests/utils.rs | 46 |
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 |
