summaryrefslogtreecommitdiff
path: root/webhook/src
diff options
context:
space:
mode:
authorMatthieu <matthieu@developershouse.xyz>2021-09-08 19:24:58 +0400
committerMatthieu <matthieu@developershouse.xyz>2021-09-08 19:24:58 +0400
commitcc498ba549de23cbbc8bf682a1495479d791148a (patch)
tree5a1437065001b5ea8a8c4aee8d2ff24563c9ca25 /webhook/src
parent125c0b5dc084e9d3fa91b55acc19c76f98ef298a (diff)
docker fixes & libsodium patch
Diffstat (limited to 'webhook/src')
-rw-r--r--webhook/src/handle.rs338
-rw-r--r--webhook/src/main.rs70
-rw-r--r--webhook/src/utils.rs96
3 files changed, 252 insertions, 252 deletions
diff --git a/webhook/src/handle.rs b/webhook/src/handle.rs
index d240d2b..d08ee1f 100644
--- a/webhook/src/handle.rs
+++ b/webhook/src/handle.rs
@@ -1,169 +1,169 @@
-use hyper::service::Service;
-use hyper::{body::to_bytes, HeaderMap};
-use hyper::{Body, Method, Request, Response, StatusCode};
-use libsodium_sys::crypto_sign_ed25519_verify_detached;
-use log::info;
-use serde_json::Value;
-use std::future;
-use std::future::Future;
-use std::pin::Pin;
-use std::str::from_utf8;
-use std::task::{Context, Poll};
-use serde::{Deserialize, Serialize};
-
-use super::utils::Settings;
-
-pub fn validate_signature(b64_public_key: &str, data: &Vec<u8>, b64_signature: &str) -> bool {
- // First, we need to check if the signature & private key is valid base64.
- let signature_result = hex::decode(b64_signature);
- let public_key_result = hex::decode(b64_public_key);
-
- if signature_result.is_ok() && public_key_result.is_ok() {
- // Since we now have the signatures in u8 vectors. We will initialize all the
- // parameters for the ffi call to sodium.
- let signature_pointer = signature_result.unwrap();
- let private_key_pointer = public_key_result.unwrap();
-
- let data_pointer = data.as_ptr();
- let data_len = data.len() as u64;
-
- // A ffi call is considered unsafe by the Rust compiler
- // we assume all the parameters are correct for the call
- unsafe {
- // If the signature is valid, sodium will return 0
- return crypto_sign_ed25519_verify_detached(
- signature_pointer.as_ptr(),
- data_pointer,
- data_len,
- private_key_pointer.as_ptr(),
- ) == 0;
- }
- }
- false
-}
-
-fn get_signature(headers: &HeaderMap) -> Option<(&str, &str)> {
- let signature = headers.get("X-Signature-Ed25519");
- let timestamp = headers.get("X-Signature-Timestamp");
-
- if signature.is_some() && timestamp.is_some() {
- return Some((
- signature.unwrap().to_str().unwrap(),
- timestamp.unwrap().to_str().unwrap(),
- ));
- }
- None
-}
-
-pub struct HandlerService {
- pub config: Settings,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct Ping {
- #[serde(rename = "type")]
- t: i32
-}
-
-impl Service<Request<Body>> for HandlerService {
- type Response = Response<Body>;
- type Error = hyper::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>> {
- Poll::Ready(Ok(()))
- }
-
- fn call(&mut self, req: Request<Body>) -> Self::Future {
- if req.method() == Method::POST {
- let public_key = self.config.discord.public_key.clone();
- return Box::pin(async move {
- let headers = req.headers().clone();
- if let Some((signature, timestamp)) = get_signature(&headers) {
- if let Ok(data) = to_bytes(req.into_body()).await {
- let contatenated_data = [timestamp.as_bytes().to_vec(), data.to_vec()].concat();
-
- if validate_signature(public_key.as_str(), &contatenated_data, signature) {
- let data: Value = serde_json::from_str(from_utf8(&data).unwrap()).unwrap();
- let t = data.get("type").unwrap().as_i64().unwrap();
-
- if t == 1 {
- info!("success!");
-
- return Ok(Response::builder().header("Content-Type", "application/json").body(serde_json::to_string(&Ping {
- t: 1
- }).unwrap().into()).unwrap());
-
- } else {
- Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("invalid operation".into()).unwrap())
- }
- } else {
- Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("signature verification failed".into()).unwrap())
- }
- } else {
- Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("failed to read body".into()).unwrap())
- }
- } else {
- Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("no signature specified".into()).unwrap())
- }
- });
- } else {
- return Box::pin(async {
- Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("bad method".into()).unwrap())
- });
- }
- }
-}
-
-pub struct MakeSvc {
- pub settings: Settings,
-}
-
-impl<T> Service<T> for MakeSvc {
- type Response = HandlerService;
- type Error = std::io::Error;
- type Future = future::Ready<Result<Self::Response, Self::Error>>;
-
- fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
- Ok(()).into()
- }
-
- fn call(&mut self, _: T) -> Self::Future {
- future::ready(Ok(HandlerService {
- config: self.settings.clone(),
- }))
- }
-}
-
-#[cfg(test)]
-mod test {
- use crate::handle::validate_signature;
-
- #[test]
- fn validate_signature_test() {
- let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
- let public_key = "eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f";
- let content = "message de test incroyable".as_bytes().to_vec();
- assert!(validate_signature(public_key, &content, signature))
- }
-
- #[test]
- fn validate_signature_reverse_test() {
- let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
- let public_key = "c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044";
- let content = "ceci est un test qui ne fonctionnera pas!"
- .as_bytes()
- .to_vec();
- assert!(!validate_signature(public_key, &content, signature))
- }
-
- #[test]
- fn invalid_hex() {
- let signature = "zzz";
- let public_key = "zzz";
- let content = "ceci est un test qui ne fonctionnera pas!"
- .as_bytes()
- .to_vec();
- assert!(!validate_signature(public_key, &content, signature))
- }
-}
+use hyper::service::Service;
+use hyper::{body::to_bytes, HeaderMap};
+use hyper::{Body, Method, Request, Response, StatusCode};
+use libsodium_sys::crypto_sign_ed25519_verify_detached;
+use log::info;
+use serde_json::Value;
+use std::future;
+use std::future::Future;
+use std::pin::Pin;
+use std::str::from_utf8;
+use std::task::{Context, Poll};
+use serde::{Deserialize, Serialize};
+
+use super::utils::Settings;
+
+pub fn validate_signature(b64_public_key: &str, data: &Vec<u8>, b64_signature: &str) -> bool {
+ // First, we need to check if the signature & private key is valid base64.
+ let signature_result = hex::decode(b64_signature);
+ let public_key_result = hex::decode(b64_public_key);
+
+ if signature_result.is_ok() && public_key_result.is_ok() {
+ // Since we now have the signatures in u8 vectors. We will initialize all the
+ // parameters for the ffi call to sodium.
+ let signature_pointer = signature_result.unwrap();
+ let private_key_pointer = public_key_result.unwrap();
+
+ let data_pointer = data.as_ptr();
+ let data_len = data.len() as u64;
+
+ // A ffi call is considered unsafe by the Rust compiler
+ // we assume all the parameters are correct for the call
+ unsafe {
+ // If the signature is valid, sodium will return 0
+ return crypto_sign_ed25519_verify_detached(
+ signature_pointer.as_ptr(),
+ data_pointer,
+ data_len,
+ private_key_pointer.as_ptr(),
+ ) == 0;
+ }
+ }
+ false
+}
+
+fn get_signature(headers: &HeaderMap) -> Option<(&str, &str)> {
+ let signature = headers.get("X-Signature-Ed25519");
+ let timestamp = headers.get("X-Signature-Timestamp");
+
+ if signature.is_some() && timestamp.is_some() {
+ return Some((
+ signature.unwrap().to_str().unwrap(),
+ timestamp.unwrap().to_str().unwrap(),
+ ));
+ }
+ None
+}
+
+pub struct HandlerService {
+ pub config: Settings,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Ping {
+ #[serde(rename = "type")]
+ t: i32
+}
+
+impl Service<Request<Body>> for HandlerService {
+ type Response = Response<Body>;
+ type Error = hyper::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>> {
+ Poll::Ready(Ok(()))
+ }
+
+ fn call(&mut self, req: Request<Body>) -> Self::Future {
+ if req.method() == Method::POST {
+ let public_key = self.config.discord.public_key.clone();
+ return Box::pin(async move {
+ let headers = req.headers().clone();
+ if let Some((signature, timestamp)) = get_signature(&headers) {
+ if let Ok(data) = to_bytes(req.into_body()).await {
+ let contatenated_data = [timestamp.as_bytes().to_vec(), data.to_vec()].concat();
+
+ if validate_signature(public_key.as_str(), &contatenated_data, signature) {
+ let data: Value = serde_json::from_str(from_utf8(&data).unwrap()).unwrap();
+ let t = data.get("type").unwrap().as_i64().unwrap();
+
+ if t == 1 {
+ info!("success!");
+
+ return Ok(Response::builder().header("Content-Type", "application/json").body(serde_json::to_string(&Ping {
+ t: 1
+ }).unwrap().into()).unwrap());
+
+ } else {
+ Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("invalid operation".into()).unwrap())
+ }
+ } else {
+ Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("signature verification failed".into()).unwrap())
+ }
+ } else {
+ Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("failed to read body".into()).unwrap())
+ }
+ } else {
+ Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("no signature specified".into()).unwrap())
+ }
+ });
+ } else {
+ return Box::pin(async {
+ Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body("bad method".into()).unwrap())
+ });
+ }
+ }
+}
+
+pub struct MakeSvc {
+ pub settings: Settings,
+}
+
+impl<T> Service<T> for MakeSvc {
+ type Response = HandlerService;
+ type Error = std::io::Error;
+ type Future = future::Ready<Result<Self::Response, Self::Error>>;
+
+ fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
+ Ok(()).into()
+ }
+
+ fn call(&mut self, _: T) -> Self::Future {
+ future::ready(Ok(HandlerService {
+ config: self.settings.clone(),
+ }))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::handle::validate_signature;
+
+ #[test]
+ fn validate_signature_test() {
+ let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
+ let public_key = "eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f";
+ let content = "message de test incroyable".as_bytes().to_vec();
+ assert!(validate_signature(public_key, &content, signature))
+ }
+
+ #[test]
+ fn validate_signature_reverse_test() {
+ let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
+ let public_key = "c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044";
+ let content = "ceci est un test qui ne fonctionnera pas!"
+ .as_bytes()
+ .to_vec();
+ assert!(!validate_signature(public_key, &content, signature))
+ }
+
+ #[test]
+ fn invalid_hex() {
+ let signature = "zzz";
+ let public_key = "zzz";
+ let content = "ceci est un test qui ne fonctionnera pas!"
+ .as_bytes()
+ .to_vec();
+ assert!(!validate_signature(public_key, &content, signature))
+ }
+}
diff --git a/webhook/src/main.rs b/webhook/src/main.rs
index ccf08ae..2daaa4d 100644
--- a/webhook/src/main.rs
+++ b/webhook/src/main.rs
@@ -1,35 +1,35 @@
-use std::net::ToSocketAddrs;
-
-use hyper::Server;
-use log::info;
-
-extern crate log;
-pub mod handle;
-pub mod utils;
-
-use handle::MakeSvc;
-use utils::{setup_program, Settings};
-
-#[tokio::main]
-async fn main() {
- setup_program("webhook");
- let config = Settings::new().unwrap();
-
- let addr = format!("{}:{}", config.server.address, config.server.port)
- .to_socket_addrs()
- .unwrap()
- .next()
- .unwrap();
-
- info!(
- "Starting server on {}:{}",
- config.server.address, config.server.port
- );
- let server = Server::bind(&addr).serve(MakeSvc {
- settings: config.clone(),
- });
-
- if let Err(e) = server.await {
- eprintln!("server error: {}", e);
- }
-}
+use std::net::ToSocketAddrs;
+
+use hyper::Server;
+use log::info;
+
+extern crate log;
+pub mod handle;
+pub mod utils;
+
+use handle::MakeSvc;
+use utils::{setup_program, Settings};
+
+#[tokio::main]
+async fn main() {
+ setup_program("webhook");
+ let config = Settings::new().unwrap();
+
+ let addr = format!("{}:{}", config.server.address, config.server.port)
+ .to_socket_addrs()
+ .unwrap()
+ .next()
+ .unwrap();
+
+ info!(
+ "Starting server on {}:{}",
+ config.server.address, config.server.port
+ );
+ let server = Server::bind(&addr).serve(MakeSvc {
+ settings: config.clone(),
+ });
+
+ if let Err(e) = server.await {
+ eprintln!("server error: {}", e);
+ }
+}
diff --git a/webhook/src/utils.rs b/webhook/src/utils.rs
index 45c84fe..442b5ba 100644
--- a/webhook/src/utils.rs
+++ b/webhook/src/utils.rs
@@ -1,48 +1,48 @@
-use std::env;
-
-use config::{Config, ConfigError, Environment, File};
-use log::info;
-use serde::Deserialize;
-
-/// Executes the required configuration steps for the program,
-/// excluding build information, Sentry and logging.
-pub fn setup_program(_name: &str) {
- pretty_env_logger::init();
-}
-
-#[derive(Debug, Deserialize, Clone)]
-pub struct Server {
- pub port: u16,
- pub address: String,
-}
-
-#[derive(Debug, Deserialize, Clone)]
-pub struct Discord {
- pub public_key: String,
- pub client_id: u32,
-}
-
-#[derive(Debug, Deserialize, Clone)]
-pub struct Settings {
- pub server: Server,
- pub discord: Discord,
-}
-
-impl Settings {
- pub fn new() -> Result<Self, ConfigError> {
- let mut default = Config::default();
- default.merge(File::with_name("config/default"))?;
- let mode = env::var("ENV").unwrap_or_else(|_| "development".into());
- info!("Configuration Environment: {}", mode);
-
- default.merge(File::with_name(&format!("config/{}", mode)).required(false))?;
- default.merge(File::with_name("config/local").required(false))?;
- default.merge(Environment::with_prefix("NOVA_GATEWAY"))?;
-
- println!("Debug mode: {:?}", default.get_bool("debug"));
-
- let config: Self = default.try_into().unwrap();
-
- Ok(config)
- }
-}
+use std::env;
+
+use config::{Config, ConfigError, Environment, File};
+use log::info;
+use serde::Deserialize;
+
+/// Executes the required configuration steps for the program,
+/// excluding build information, Sentry and logging.
+pub fn setup_program(_name: &str) {
+ pretty_env_logger::init();
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct Server {
+ pub port: u16,
+ pub address: String,
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct Discord {
+ pub public_key: String,
+ pub client_id: u32,
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct Settings {
+ pub server: Server,
+ pub discord: Discord,
+}
+
+impl Settings {
+ pub fn new() -> Result<Self, ConfigError> {
+ let mut default = Config::default();
+ default.merge(File::with_name("config/default"))?;
+ let mode = env::var("ENV").unwrap_or_else(|_| "development".into());
+ info!("Configuration Environment: {}", mode);
+
+ default.merge(File::with_name(&format!("config/{}", mode)).required(false))?;
+ default.merge(File::with_name("config/local").required(false))?;
+ default.merge(Environment::with_prefix("NOVA_GATEWAY"))?;
+
+ println!("Debug mode: {:?}", default.get_bool("debug"));
+
+ let config: Self = default.try_into().unwrap();
+
+ Ok(config)
+ }
+}