From 322fa93c65d01f4e94bc56d4ca9a580d80337bf1 Mon Sep 17 00:00:00 2001 From: Mo8it Date: Wed, 12 Oct 2022 13:19:54 +0200 Subject: [PATCH] Split code into more modules --- migrations/.keep | 0 src/db.rs | 5 +- src/guards.rs | 66 ++++++++++++++++++++++++ src/main.rs | 132 ++--------------------------------------------- src/models.rs | 3 +- src/routes.rs | 61 ++++++++++++++++++++++ 6 files changed, 137 insertions(+), 130 deletions(-) create mode 100644 migrations/.keep create mode 100644 src/guards.rs create mode 100644 src/routes.rs diff --git a/migrations/.keep b/migrations/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/db.rs b/src/db.rs index aebdb9b..4ba38d8 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,8 +1,9 @@ +use diesel::prelude::*; +use std::process::Output; + use crate::config::Hook; use crate::models::{HookLog, NewHookLog}; use crate::schema::hooklog; -use diesel::prelude::*; -use std::process::Output; fn establish_connection() -> SqliteConnection { let database_url = "db/db.sqlite"; diff --git a/src/guards.rs b/src/guards.rs new file mode 100644 index 0000000..5125e3e --- /dev/null +++ b/src/guards.rs @@ -0,0 +1,66 @@ +use hmac::{Hmac, Mac}; +use rocket::data::{Data, FromData, Limits, Outcome}; +use rocket::http::Status; +use rocket::request::{self, Request}; +use serde_json::Value; +use sha2::Sha256; + +use crate::config; + +pub struct Repo<'r> { + pub clone_url: &'r str, +} + +#[derive(Debug)] +pub enum RepoDataGuardError { + PayloadTooLarge, + MissingSignature, + MoreThatOneSignature, + InvalidSignature, + Io(std::io::Error), +} + +#[rocket::async_trait] +impl<'r> FromData<'r> for Repo<'r> { + type Error = RepoDataGuardError; + + async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { + let payload = match data.open(Limits::JSON).into_bytes().await { + Ok(payload) if payload.is_complete() => payload.into_inner(), + Ok(_) => { + return Outcome::Failure((Status::PayloadTooLarge, Self::Error::PayloadTooLarge)) + } + Err(e) => return Outcome::Failure((Status::InternalServerError, Self::Error::Io(e))), + }; + + let mut received_signatures = req.headers().get("X-GITEA-SIGNATURE"); + let received_signature = match received_signatures.next() { + Some(signature) => hex::decode(signature).unwrap(), + None => return Outcome::Failure((Status::BadRequest, Self::Error::MissingSignature)), + }; + + if received_signatures.next().is_some() { + return Outcome::Failure((Status::BadRequest, Self::Error::MoreThatOneSignature)); + } + + if !is_valid_signature(&received_signature, &payload) { + return Outcome::Failure((Status::BadRequest, Self::Error::InvalidSignature)); + } + + let json: Value = serde_json::from_slice(&payload).unwrap(); + let repo = json.get("repository").unwrap(); + let clone_url = repo.get("clone_url").unwrap().as_str().unwrap().to_string(); + + let clone_url = request::local_cache!(req, clone_url); + + Outcome::Success(Repo { clone_url }) + } +} + +fn is_valid_signature(received_signature: &[u8], payload: &[u8]) -> bool { + let mut mac = Hmac::::new_from_slice(&config::get_secret()).unwrap(); + mac.update(payload); + let expected_signature = mac.finalize().into_bytes(); + + received_signature[..] == expected_signature[..] +} diff --git a/src/main.rs b/src/main.rs index b56ee2c..bf39819 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,135 +1,13 @@ -#[macro_use] -extern crate rocket; - mod config; mod db; +mod guards; mod models; +mod routes; mod schema; -use hmac::{Hmac, Mac}; -use rocket::data::{self, Data, FromData, Limits, Outcome}; -use rocket::http::Status; -use rocket::request::{self, Request}; -use serde_json::Value; -use sha2::Sha256; -use std::process::Command; - -struct Repo<'r> { - clone_url: &'r str, -} - -fn is_valid_signature(received_signature: &[u8], payload: &[u8]) -> bool { - let mut mac = Hmac::::new_from_slice(&config::get_secret()).unwrap(); - mac.update(payload); - let expected_signature = mac.finalize().into_bytes(); - - received_signature[..] == expected_signature[..] -} - -#[derive(Debug)] -enum RepoDataGuardError { - PayloadTooLarge, - MissingSignature, - MoreThatOneSignature, - InvalidSignature, - Io(std::io::Error), -} - -#[rocket::async_trait] -impl<'r> FromData<'r> for Repo<'r> { - type Error = RepoDataGuardError; - - async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> { - let payload = match data.open(Limits::JSON).into_bytes().await { - Ok(payload) if payload.is_complete() => payload.into_inner(), - Ok(_) => { - return Outcome::Failure((Status::PayloadTooLarge, Self::Error::PayloadTooLarge)) - } - Err(e) => return Outcome::Failure((Status::InternalServerError, Self::Error::Io(e))), - }; - - let mut received_signatures = req.headers().get("X-GITEA-SIGNATURE"); - let received_signature = match received_signatures.next() { - Some(signature) => hex::decode(signature).unwrap(), - None => return Outcome::Failure((Status::BadRequest, Self::Error::MissingSignature)), - }; - - if received_signatures.next().is_some() { - return Outcome::Failure((Status::BadRequest, Self::Error::MoreThatOneSignature)); - } - - if !is_valid_signature(&received_signature, &payload) { - return Outcome::Failure((Status::BadRequest, Self::Error::InvalidSignature)); - } - - let json: Value = serde_json::from_slice(&payload).unwrap(); - let repo = json.get("repository").unwrap(); - let clone_url = repo.get("clone_url").unwrap().as_str().unwrap().to_string(); - - let clone_url = request::local_cache!(req, clone_url); - - Outcome::Success(Repo { clone_url }) - } -} - -#[get("/")] -fn index(id: i32) -> String { - let hook_log = db::get_hook_log(id); - format!( - "Hook log id: -{} - -Repository url: -{} - -Command with arguments: -{} - -Current directory of the command: -{} - -Standard output: -{} - -Standard error: -{} - -Status code: -{} -", - hook_log.id, - hook_log.repo_url, - hook_log.command_with_args, - hook_log.current_dir, - hook_log.stdout, - hook_log.stderr, - match hook_log.status_code { - Some(code) => code.to_string(), - None => String::from("None"), - } - ) -} - -#[post("/trigger", format = "json", data = "")] -fn trigger(repo: Repo) -> String { - let hook = config::get_hook(repo.clone_url).unwrap(); - - let output = Command::new(&hook.command) - .args(&hook.args) - .current_dir(&hook.current_dir) - .output() - .unwrap(); - - let new_hook_log_id = db::add_hook_log(&hook, &output); - - let base_url = config::get_base_url(); - - format!("https://{base_url}/{new_hook_log_id}") -} - -#[launch] +#[rocket::launch] fn rocket() -> _ { rocket::build() - .mount("/", routes![index]) - .mount("/api", routes![trigger]) + .mount("/", rocket::routes![routes::index]) + .mount("/api", rocket::routes![routes::trigger]) } diff --git a/src/models.rs b/src/models.rs index 93407e4..9aecdac 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,6 +1,7 @@ -use crate::schema::hooklog; use diesel::prelude::{Insertable, Queryable}; +use crate::schema::hooklog; + #[derive(Queryable)] pub struct HookLog { pub id: i32, diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 0000000..63719da --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,61 @@ +use rocket::{get, post}; +use std::process::Command; + +use crate::config; +use crate::db; +use crate::guards; + +#[get("/")] +pub fn index(id: i32) -> String { + let hook_log = db::get_hook_log(id); + format!( + "Hook log id: +{} + +Repository url: +{} + +Command with arguments: +{} + +Current directory of the command: +{} + +Standard output: +{} + +Standard error: +{} + +Status code: +{} +", + hook_log.id, + hook_log.repo_url, + hook_log.command_with_args, + hook_log.current_dir, + hook_log.stdout, + hook_log.stderr, + match hook_log.status_code { + Some(code) => code.to_string(), + None => String::from("None"), + } + ) +} + +#[post("/trigger", format = "json", data = "")] +pub fn trigger(repo: guards::Repo) -> String { + let hook = config::get_hook(repo.clone_url).unwrap(); + + let output = Command::new(&hook.command) + .args(&hook.args) + .current_dir(&hook.current_dir) + .output() + .unwrap(); + + let new_hook_log_id = db::add_hook_log(&hook, &output); + + let base_url = config::get_base_url(); + + format!("https://{base_url}/{new_hook_log_id}") +}