summaryrefslogtreecommitdiff
path: root/webhook/src
diff options
context:
space:
mode:
Diffstat (limited to 'webhook/src')
-rw-r--r--webhook/src/handler/handler.rs16
-rw-r--r--webhook/src/handler/make_service.rs2
-rw-r--r--webhook/src/handler/mod.rs3
-rw-r--r--webhook/src/handler/tests/handler.rs174
-rw-r--r--webhook/src/handler/tests/mod.rs1
-rw-r--r--webhook/src/main.rs11
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);
}
}