diff options
Diffstat (limited to 'webhook/src')
| -rw-r--r-- | webhook/src/handler/handler.rs | 16 | ||||
| -rw-r--r-- | webhook/src/handler/make_service.rs | 2 | ||||
| -rw-r--r-- | webhook/src/handler/mod.rs | 3 | ||||
| -rw-r--r-- | webhook/src/handler/tests/handler.rs | 174 | ||||
| -rw-r--r-- | webhook/src/handler/tests/mod.rs | 1 | ||||
| -rw-r--r-- | webhook/src/main.rs | 11 |
6 files changed, 190 insertions, 17 deletions
diff --git a/webhook/src/handler/handler.rs b/webhook/src/handler/handler.rs index 9545bec..b3dc8a6 100644 --- a/webhook/src/handler/handler.rs +++ b/webhook/src/handler/handler.rs @@ -5,17 +5,10 @@ use hyper::{ service::Service, Body, Method, Request, Response, StatusCode, }; -use log::{error, info, trace}; -use nats::Connection; +use common::log::{error, info, trace}; +use common::nats_crate::Connection; use serde::{Deserialize, Serialize}; -use std::{ - future::Future, - io::{Error, ErrorKind}, - pin::Pin, - str::from_utf8, - sync::Arc, - task::{Context, Poll}, -}; +use std::{future::Future, io::{Error, ErrorKind}, pin::Pin, str::from_utf8, sync::Arc, task::{Context, Poll}, time::Duration}; /// Hyper service used to handle the discord webhooks #[derive(Clone)] @@ -105,13 +98,14 @@ impl Service<Request<Body>> for HandlerService { node_id: "".to_string(), span: None, }, + operation: "".to_string(), data: value, }) .unwrap(); match self_clone .nats - .request("nova.cache.dispatch.interaction", payload) + .request_timeout("nova.cache.dispatch.interaction", payload, Duration::from_secs(2)) { Ok(response) => Ok(Response::builder() .header("Content-Type", "application/json") diff --git a/webhook/src/handler/make_service.rs b/webhook/src/handler/make_service.rs index 0778ad0..deeb2fe 100644 --- a/webhook/src/handler/make_service.rs +++ b/webhook/src/handler/make_service.rs @@ -1,7 +1,7 @@ use super::handler::HandlerService; use crate::config::Config; use hyper::service::Service; -use nats::Connection; +use common::nats_crate::Connection; use std::{ future::{ready, Ready}, sync::Arc, diff --git a/webhook/src/handler/mod.rs b/webhook/src/handler/mod.rs index 490c580..598906b 100644 --- a/webhook/src/handler/mod.rs +++ b/webhook/src/handler/mod.rs @@ -1,4 +1,5 @@ pub mod make_service; mod signature; mod handler; -mod types;
\ No newline at end of file +mod types; +pub mod tests;
\ No newline at end of file diff --git a/webhook/src/handler/tests/handler.rs b/webhook/src/handler/tests/handler.rs new file mode 100644 index 0000000..3e5ccd0 --- /dev/null +++ b/webhook/src/handler/tests/handler.rs @@ -0,0 +1,174 @@ +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); +} + +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); +} + +#[tokio::test] +async fn respond_to_pings_and_deny_invalid() { + use crate::start; + use common::config::test_init; + use common::config::Settings; + use common::log::info; + use common::testcontainers::images::generic::GenericImage; + use common::testcontainers::Docker; + use hyper::{Body, Method, Request}; + use libsodium_sys::sodium_init; + use serde_json::json; + use std::time::Duration; + + test_init(); + + unsafe { + if sodium_init() < 0 { + panic!("libsodium init error!"); + } + } + + let (private_key, secret_key) = generate_keypair(); + 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, secret_key); + + // start nats + let docker = common::testcontainers::clients::Cli::default(); + let image = GenericImage::new("nats"); + let node = docker.run(image); + node.start(); + let port = node.get_host_port(4222).unwrap(); + + let settings: Settings<crate::config::Config> = 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: private_key, + client_id: 0, + }, + }, + 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), + }, + }; + + let nats: common::nats_crate::Connection = settings.nats.clone().into(); + // start the server + tokio::task::spawn(start(settings)); + tokio::time::sleep(Duration::from_secs(1)).await; + + 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); + + 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); + + // setup nats mock listener + 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, secret_key); + + // we must timeout + let req = Request::builder() + .method(Method::POST) + .uri("http://localhost:5003/") + .header("X-Signature-Ed25519", signature.clone()) + .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); + + 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.clone()) + .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); +} diff --git a/webhook/src/handler/tests/mod.rs b/webhook/src/handler/tests/mod.rs new file mode 100644 index 0000000..ef7d850 --- /dev/null +++ b/webhook/src/handler/tests/mod.rs @@ -0,0 +1 @@ +pub mod handler;
\ No newline at end of file diff --git a/webhook/src/main.rs b/webhook/src/main.rs index 6ea22fc..b213e9d 100644 --- a/webhook/src/main.rs +++ b/webhook/src/main.rs @@ -5,14 +5,16 @@ use crate::handler::make_service::MakeSvc; use crate::config::Config;
use common::config::Settings;
+use common::log::{error, info};
use hyper::Server;
-use log::{error, info};
#[tokio::main]
async fn main() {
let settings: Settings<Config> = Settings::new("webhook").unwrap();
- println!("{:?}", settings);
+ start(settings).await;
+}
+async fn start(settings: Settings<Config>) {
let addr = format!(
"{}:{}",
settings.config.server.address, settings.config.server.port
@@ -27,12 +29,13 @@ async fn main() { settings.config.server.address, settings.config.server.port
);
+ let config = Arc::new(settings.config);
let server = Server::bind(&addr).serve(MakeSvc {
- settings: settings.config.clone(),
+ settings: config,
nats: Arc::new(settings.nats.into()),
});
if let Err(e) = server.await {
- error!("server error: {}", e);
+ panic!("server error: {}", e);
}
}
|
