summaryrefslogtreecommitdiff
path: root/exes/webhook
diff options
context:
space:
mode:
Diffstat (limited to 'exes/webhook')
-rw-r--r--exes/webhook/src/config.rs2
-rw-r--r--exes/webhook/src/handler/error.rs36
-rw-r--r--exes/webhook/src/handler/make_service.rs2
-rw-r--r--exes/webhook/src/handler/mod.rs140
-rw-r--r--exes/webhook/src/handler/signature.rs9
-rw-r--r--exes/webhook/src/handler/tests/signature.rs18
-rw-r--r--exes/webhook/src/lib.rs16
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(