summaryrefslogtreecommitdiff
path: root/webhook/src
diff options
context:
space:
mode:
Diffstat (limited to 'webhook/src')
-rw-r--r--webhook/src/handler/error.rs14
-rw-r--r--webhook/src/handler/handler.rs84
-rw-r--r--webhook/src/handler/tests/handler_integration.rs35
3 files changed, 84 insertions, 49 deletions
diff --git a/webhook/src/handler/error.rs b/webhook/src/handler/error.rs
index f998b84..d4fee07 100644
--- a/webhook/src/handler/error.rs
+++ b/webhook/src/handler/error.rs
@@ -1,4 +1,4 @@
-use hyper::{Body, Response, StatusCode};
+use hyper::{header::ToStrError, Body, Response, StatusCode};
pub struct WebhookError {
pub code: StatusCode,
@@ -22,3 +22,15 @@ impl Into<Response<Body>> for WebhookError {
.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/webhook/src/handler/handler.rs b/webhook/src/handler/handler.rs
index e54b5ee..97300e4 100644
--- a/webhook/src/handler/handler.rs
+++ b/webhook/src/handler/handler.rs
@@ -1,10 +1,10 @@
use super::error::WebhookError;
use super::signature::validate_signature;
use crate::config::Config;
-use common::discord_models::slash_commands::{Interaction, InteractionRequestType};
use common::log::{debug, error};
use common::nats_crate::Connection;
-use common::payloads::CacheData;
+use common::payloads::SerializeHelper;
+use ed25519_dalek::PublicKey;
use hyper::{
body::{to_bytes, Bytes},
service::Service,
@@ -19,54 +19,50 @@ use std::{
task::{Context, Poll},
time::Duration,
};
-use ed25519_dalek::PublicKey;
+use twilight_model::application::interaction::Interaction;
+use twilight_model::gateway::event::Event;
+use twilight_model::gateway::payload::InteractionCreate;
/// Hyper service used to handle the discord webhooks
#[derive(Clone)]
pub struct HandlerService {
pub config: Arc<Config>,
pub nats: Arc<Connection>,
- pub public_key: Arc<PublicKey>
+ pub public_key: Arc<PublicKey>,
}
impl HandlerService {
async fn check_request(&self, req: Request<Body>) -> Result<Bytes, WebhookError> {
if req.method() == Method::POST {
- let headers = req.headers().clone();
- let signature = headers.get("X-Signature-Ed25519");
- let timestamp = headers.get("X-Signature-Timestamp");
- if let (Some(timestamp), Some(signature)) = (timestamp, signature) {
- if let Ok(data) = to_bytes(req.into_body()).await {
- let contatenated_data = [timestamp.as_bytes().to_vec(), data.to_vec()].concat();
- if let Ok(signature_str) = &signature.to_str() {
- if validate_signature(
- &self.public_key,
- &contatenated_data,
- signature_str,
- ) {
- Ok(data)
- } else {
- Err(WebhookError::new(
- StatusCode::UNAUTHORIZED,
- "invalid signature",
- ))
- }
- } else {
- Err(WebhookError::new(
- StatusCode::BAD_REQUEST,
- "failed to read signature",
- ))
- }
- } else {
- Err(WebhookError::new(
- StatusCode::BAD_REQUEST,
- "unable to read body",
- ))
- }
+ let signature = if let Some(sig) = req.headers().get("X-Signature-Ed25519") {
+ sig.to_owned()
+ } else {
+ return Err(WebhookError::new(
+ StatusCode::BAD_REQUEST,
+ "missing signature header",
+ ));
+ };
+
+ let timestamp = if let Some(timestamp) = req.headers().get("X-Signature-Timestamp") {
+ timestamp.to_owned()
+ } else {
+ return Err(WebhookError::new(
+ StatusCode::BAD_REQUEST,
+ "missing timestamp header",
+ ));
+ };
+ let data = to_bytes(req.into_body()).await?;
+
+ if validate_signature(
+ &self.public_key,
+ &[timestamp.as_bytes().to_vec(), data.to_vec()].concat(),
+ signature.to_str()?,
+ ) {
+ Ok(data)
} else {
Err(WebhookError::new(
StatusCode::UNAUTHORIZED,
- "missing signature headers",
+ "invalid signature",
))
}
} else {
@@ -83,8 +79,8 @@ impl HandlerService {
let utf8 = from_utf8(&data);
match utf8 {
Ok(data) => match serde_json::from_str::<Interaction>(data) {
- Ok(value) => match value.type_ {
- InteractionRequestType::Ping => Ok(Response::builder()
+ Ok(value) => match value {
+ Interaction::Ping(_) => Ok(Response::builder()
.header("Content-Type", "application/json")
.body(serde_json::to_string(&Ping { t: 1 }).unwrap().into())
.unwrap()),
@@ -97,9 +93,9 @@ impl HandlerService {
node_id: "".to_string(),
span: None,
},
- data: CacheData::InteractionCreate {
- interaction: Box::new(value),
- },
+ data: SerializeHelper(Event::InteractionCreate(
+ Box::new(InteractionCreate(value)),
+ )),
})
.unwrap();
@@ -127,10 +123,10 @@ impl HandlerService {
Err(error) => {
error!("invalid json body: {}", error);
Err(WebhookError::new(
- StatusCode::BAD_REQUEST,
- "invalid json body",
+ StatusCode::BAD_REQUEST,
+ "invalid json body",
))
- },
+ }
},
Err(_) => Err(WebhookError::new(StatusCode::BAD_REQUEST, "not utf-8 body")),
diff --git a/webhook/src/handler/tests/handler_integration.rs b/webhook/src/handler/tests/handler_integration.rs
index 906b347..b5204fb 100644
--- a/webhook/src/handler/tests/handler_integration.rs
+++ b/webhook/src/handler/tests/handler_integration.rs
@@ -101,7 +101,7 @@ unsafe fn destroy() {
#[tokio::test]
async fn respond_to_pings() {
- let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string();
+ let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1, "channel_id": "123" }).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, &TEST_KEYPAIR);
@@ -113,6 +113,7 @@ async fn respond_to_pings() {
.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();
@@ -121,7 +122,7 @@ async fn respond_to_pings() {
#[tokio::test]
async fn deny_invalid_signatures() {
- let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string();
+ let ping = json!({ "type": 1, "id": "0", "application_id": "0", "token": "random token", "version": 1, "channel_id": "123" }).to_string();
let timestamp = "my datetime :)";
let req = Request::builder()
@@ -138,7 +139,19 @@ async fn deny_invalid_signatures() {
#[tokio::test]
async fn response_500_when_no_nats_response() {
- let ping = json!({ "type": 2, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string();
+ let ping = json!({
+ "type": 2,
+ "id": "0",
+ "application_id": "0",
+ "token": "random token",
+ "version": 1,
+ "channel_id": "123",
+ "data": {
+ "id": "0",
+ "name": "command"
+ }
+ }).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, &TEST_KEYPAIR);
@@ -164,7 +177,19 @@ async fn respond_from_nats_response() {
nats = SETTINGS.clone().unwrap().nats.into();
}
let sub = nats.subscribe("nova.cache.dispatch.interaction").unwrap();
- let ping = json!({ "type": 2, "id": "0", "application_id": "0", "token": "random token", "version": 1 }).to_string();
+ let ping = json!({
+ "type": 2,
+ "id": "0",
+ "application_id": "0",
+ "token": "random token",
+ "version": 1,
+ "channel_id": "123",
+ "data": {
+ "id": "0",
+ "name": "command"
+ }
+ }).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, &TEST_KEYPAIR);
@@ -182,6 +207,7 @@ async fn respond_from_nats_response() {
.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_eq!(result.status(), StatusCode::OK);
@@ -201,6 +227,7 @@ async fn response_400_when_invalid_json_body() {
.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_eq!(result.status(), StatusCode::BAD_REQUEST);