diff options
Diffstat (limited to 'exes/webhook')
| -rw-r--r-- | exes/webhook/src/config.rs | 2 | ||||
| -rw-r--r-- | exes/webhook/src/handler/error.rs | 36 | ||||
| -rw-r--r-- | exes/webhook/src/handler/make_service.rs | 2 | ||||
| -rw-r--r-- | exes/webhook/src/handler/mod.rs | 140 | ||||
| -rw-r--r-- | exes/webhook/src/handler/signature.rs | 9 | ||||
| -rw-r--r-- | exes/webhook/src/handler/tests/signature.rs | 18 | ||||
| -rw-r--r-- | exes/webhook/src/lib.rs | 16 |
7 files changed, 80 insertions, 143 deletions
diff --git a/exes/webhook/src/config.rs b/exes/webhook/src/config.rs index b96f368..ccc7894 100644 --- a/exes/webhook/src/config.rs +++ b/exes/webhook/src/config.rs @@ -35,7 +35,7 @@ pub struct Discord { } #[derive(Debug, Deserialize, Clone, Default, Copy)] -pub struct WebhookConfig { +pub struct Webhook { pub server: ServerSettings, pub discord: Discord, } diff --git a/exes/webhook/src/handler/error.rs b/exes/webhook/src/handler/error.rs deleted file mode 100644 index ffa4cca..0000000 --- a/exes/webhook/src/handler/error.rs +++ /dev/null @@ -1,36 +0,0 @@ -use hyper::{header::ToStrError, Body, Response, StatusCode}; - -pub struct WebhookError { - pub code: StatusCode, - pub message: String, -} - -impl WebhookError { - pub fn new(code: StatusCode, message: &str) -> WebhookError { - WebhookError { - code, - message: message.to_string(), - } - } -} - -impl From<WebhookError> for Response<Body> { - fn from(value: WebhookError) -> Self { - Response::builder() - .status(value.code) - .body(value.message.into()) - .unwrap() - } -} - -impl From<hyper::Error> for WebhookError { - fn from(_: hyper::Error) -> Self { - WebhookError::new(StatusCode::BAD_REQUEST, "invalid request") - } -} - -impl From<ToStrError> for WebhookError { - fn from(_: ToStrError) -> Self { - WebhookError::new(StatusCode::BAD_REQUEST, "invalid request") - } -} diff --git a/exes/webhook/src/handler/make_service.rs b/exes/webhook/src/handler/make_service.rs index b51494a..4202cd7 100644 --- a/exes/webhook/src/handler/make_service.rs +++ b/exes/webhook/src/handler/make_service.rs @@ -23,7 +23,7 @@ impl<T, V: Clone> Service<T> for MakeSvc<V> { } impl<T: Clone> MakeSvc<T> { - pub fn new(service: T) -> Self { + pub const fn new(service: T) -> Self { Self { service } } } diff --git a/exes/webhook/src/handler/mod.rs b/exes/webhook/src/handler/mod.rs index 594919b..ea7ecca 100644 --- a/exes/webhook/src/handler/mod.rs +++ b/exes/webhook/src/handler/mod.rs @@ -1,18 +1,17 @@ -use crate::config::WebhookConfig; +use crate::config::Webhook; +use anyhow::bail; use async_nats::Client; use ed25519_dalek::PublicKey; -use error::WebhookError; use hyper::{ body::{to_bytes, Bytes}, service::Service, - Body, Method, Request, Response, StatusCode, + Body, Method, Request, Response, }; use shared::payloads::{CachePayload, DispatchEventTagged}; -use signature::validate_signature; +use signature::validate; use std::{ future::Future, pin::Pin, - str::from_utf8, task::{Context, Poll}, }; use tracing::{debug, error}; @@ -22,7 +21,6 @@ use twilight_model::{ gateway::payload::incoming::InteractionCreate, }; -mod error; pub mod make_service; mod signature; @@ -32,46 +30,37 @@ pub mod tests; /// Hyper service used to handle the discord webhooks #[derive(Clone)] pub struct WebhookService { - pub config: WebhookConfig, + pub config: Webhook, pub nats: Client, } impl WebhookService { - async fn check_request(req: Request<Body>, pk: PublicKey) -> Result<Bytes, WebhookError> { + async fn check_request(req: Request<Body>, pk: PublicKey) -> Result<Bytes, anyhow::Error> { if req.method() == Method::POST { let signature = if let Some(sig) = req.headers().get("X-Signature-Ed25519") { - sig.to_owned() + sig.clone() } else { - return Err(WebhookError::new( - StatusCode::BAD_REQUEST, - "missing signature header", - )); + bail!("Missing signature header"); }; let timestamp = if let Some(timestamp) = req.headers().get("X-Signature-Timestamp") { - timestamp.to_owned() + timestamp.clone() } else { - return Err(WebhookError::new( - StatusCode::BAD_REQUEST, - "missing timestamp header", - )); + bail!("Missing timestamp header"); }; let data = to_bytes(req.into_body()).await?; - if validate_signature( + if validate( &pk, &[timestamp.as_bytes().to_vec(), data.to_vec()].concat(), signature.to_str()?, ) { Ok(data) } else { - Err(WebhookError::new( - StatusCode::UNAUTHORIZED, - "invalid signature", - )) + bail!("invalid signature"); } } else { - Err(WebhookError::new(StatusCode::NOT_FOUND, "not found")) + bail!("not found"); } } @@ -79,69 +68,46 @@ impl WebhookService { req: Request<Body>, nats: Client, pk: PublicKey, - ) -> Result<Response<Body>, WebhookError> { - match Self::check_request(req, pk).await { - Ok(data) => { - let utf8 = from_utf8(&data); - match utf8 { - Ok(data) => match serde_json::from_str::<Interaction>(data) { - Ok(value) => { - match value.kind { - InteractionType::Ping => Ok(Response::builder() - .header("Content-Type", "application/json") - .body(r#"{"type":1}"#.into()) - .unwrap()), - _ => { - debug!("calling nats"); - // this should hopefully not fail ? - - let data = CachePayload { - data: DispatchEventTagged { - data: DispatchEvent::InteractionCreate(Box::new( - InteractionCreate(value), - )), - }, - }; - - let payload = serde_json::to_string(&data).unwrap(); - - match nats - .request( - "nova.cache.dispatch.INTERACTION_CREATE".to_string(), - Bytes::from(payload), - ) - .await - { - Ok(response) => Ok(Response::builder() - .header("Content-Type", "application/json") - .body(Body::from(response.payload)) - .unwrap()), - - Err(error) => { - error!("failed to request nats: {}", error); - Err(WebhookError::new( - StatusCode::INTERNAL_SERVER_ERROR, - "failed to request nats", - )) - } - } - } - } - } - - Err(error) => { - error!("invalid json body: {}", error); - Err(WebhookError::new( - StatusCode::BAD_REQUEST, - "invalid json body", - )) - } - }, - - Err(_) => Err(WebhookError::new(StatusCode::BAD_REQUEST, "not utf-8 body")), + ) -> Result<Response<Body>, anyhow::Error> { + let data = Self::check_request(req, pk).await?; + let interaction: Interaction = serde_json::from_slice(&data)?; + + if interaction.kind == InteractionType::Ping { + Ok(Response::builder() + .header("Content-Type", "application/json") + .body(r#"{"type":1}"#.into()) + .unwrap()) + } else { + debug!("calling nats"); + // this should hopefully not fail ? + + let data = CachePayload { + data: DispatchEventTagged { + data: DispatchEvent::InteractionCreate(Box::new(InteractionCreate( + interaction, + ))), + }, + }; + + let payload = serde_json::to_string(&data).unwrap(); + + match nats + .request( + "nova.cache.dispatch.INTERACTION_CREATE".to_string(), + Bytes::from(payload), + ) + .await + { + Ok(response) => Ok(Response::builder() + .header("Content-Type", "application/json") + .body(Body::from(response.payload)) + .unwrap()), + + Err(error) => { + error!("failed to request nats: {}", error); + Err(anyhow::anyhow!("internal error")) } } - Err(error) => Err(error), } } } @@ -149,7 +115,7 @@ impl WebhookService { /// Implementation of the service impl Service<hyper::Request<Body>> for WebhookService { type Response = hyper::Response<Body>; - type Error = hyper::Error; + type Error = anyhow::Error; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> { @@ -163,7 +129,7 @@ impl Service<hyper::Request<Body>> for WebhookService { match response { Ok(r) => Ok(r), - Err(e) => Ok(e.into()), + Err(e) => Err(e), } }) } diff --git a/exes/webhook/src/handler/signature.rs b/exes/webhook/src/handler/signature.rs index 05221d3..5a48645 100644 --- a/exes/webhook/src/handler/signature.rs +++ b/exes/webhook/src/handler/signature.rs @@ -1,14 +1,13 @@ use ed25519_dalek::{PublicKey, Signature, Verifier}; #[inline] -pub fn validate_signature(public_key: &PublicKey, data: &[u8], hex_signature: &str) -> bool { +pub fn validate(public_key: &PublicKey, data: &[u8], hex_signature: &str) -> bool { let mut slice: [u8; Signature::BYTE_SIZE] = [0; Signature::BYTE_SIZE]; let signature_result = hex::decode_to_slice(hex_signature, &mut slice); - let mut result = false; if signature_result.is_ok() { - result = public_key.verify(data, &Signature::from(slice)).is_ok(); + public_key.verify(data, &Signature::from(slice)).is_ok() + } else { + false } - - result } diff --git a/exes/webhook/src/handler/tests/signature.rs b/exes/webhook/src/handler/tests/signature.rs index 0bed86a..4ff52ff 100644 --- a/exes/webhook/src/handler/tests/signature.rs +++ b/exes/webhook/src/handler/tests/signature.rs @@ -1,16 +1,16 @@ -use crate::handler::signature::validate_signature; +use crate::handler::signature::validate; use ed25519_dalek::PublicKey; #[test] fn validate_signature_test() { let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002"; - let content = "message de test incroyable".as_bytes().to_vec(); + let content = b"message de test incroyable"; let public_key = PublicKey::from_bytes( &hex::decode("eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f").unwrap(), ) .unwrap(); - assert!(validate_signature(&public_key, &content, signature)) + assert!(validate(&public_key, content, signature)); } #[test] @@ -21,10 +21,8 @@ fn validate_signature_reverse_test() { ) .unwrap(); - let content = "ceci est un test qui ne fonctionnera pas!" - .as_bytes() - .to_vec(); - assert!(!validate_signature(&public_key, &content, signature)) + let content = b"ceci est un test qui ne fonctionnera pas!"; + assert!(!validate(&public_key, content, signature)); } #[test] @@ -35,8 +33,6 @@ fn invalid_hex() { ) .unwrap(); - let content = "ceci est un test qui ne fonctionnera pas!" - .as_bytes() - .to_vec(); - assert!(!validate_signature(&public_key, &content, signature)) + let content = b"ceci est un test qui ne fonctionnera pas!"; + assert!(!validate(&public_key, content, signature)); } diff --git a/exes/webhook/src/lib.rs b/exes/webhook/src/lib.rs index 057e70f..933f38e 100644 --- a/exes/webhook/src/lib.rs +++ b/exes/webhook/src/lib.rs @@ -1,9 +1,21 @@ +#![deny( + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + unsafe_code +)] + mod config; mod handler; use std::{future::Future, pin::Pin}; use crate::{ - config::WebhookConfig, + config::Webhook, handler::{make_service::MakeSvc, WebhookService}, }; use async_nats::Client; @@ -16,7 +28,7 @@ use tracing::info; pub struct WebhookServer {} impl Component for WebhookServer { - type Config = WebhookConfig; + type Config = Webhook; const SERVICE_NAME: &'static str = "webhook"; fn start( |
