mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-28 19:59:46 -05:00
github build listener
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1826,6 +1826,7 @@ dependencies = [
|
||||
"dotenv",
|
||||
"envy",
|
||||
"futures",
|
||||
"hex",
|
||||
"hmac",
|
||||
"jwt",
|
||||
"log",
|
||||
|
||||
@@ -47,6 +47,7 @@ jwt = "0.16"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
bcrypt = "0.14"
|
||||
hex = "0.4"
|
||||
aws-config = "0.55"
|
||||
aws-sdk-ec2 = "0.28"
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
@@ -36,6 +36,7 @@ jwt.workspace = true
|
||||
hmac.workspace = true
|
||||
sha2.workspace = true
|
||||
bcrypt.workspace = true
|
||||
hex.workspace = true
|
||||
async-trait.workspace = true
|
||||
futures.workspace = true
|
||||
aws-config.workspace = true
|
||||
|
||||
128
core/src/github_listener.rs
Normal file
128
core/src/github_listener.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Path, http::HeaderMap, routing::post, Router};
|
||||
use hex::ToHex;
|
||||
use hmac::{Hmac, Mac};
|
||||
use monitor_types::requests::api;
|
||||
use resolver_api::Resolve;
|
||||
use serde::Deserialize;
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::{
|
||||
auth::InnerRequestUser,
|
||||
helpers::random_duration,
|
||||
state::{State, StateExtension},
|
||||
};
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Id {
|
||||
id: String,
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new().route(
|
||||
"/build/:id",
|
||||
post(
|
||||
|state: StateExtension, Path(Id { id }), headers: HeaderMap, body: String| async move {
|
||||
tokio::spawn(async move {
|
||||
let res = state.handle_build_webhook(id.clone(), headers, body).await;
|
||||
if let Err(e) = res {
|
||||
warn!("failed to run build webook for build {id} | {e:#?}");
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn handle_build_webhook(
|
||||
&self,
|
||||
build_id: String,
|
||||
headers: HeaderMap,
|
||||
body: String,
|
||||
) -> anyhow::Result<()> {
|
||||
self.verify_gh_signature(headers, &body).await?;
|
||||
let request_branch = extract_branch(&body)?;
|
||||
let expected_branch = self.get_build(&build_id).await?.config.branch;
|
||||
if request_branch != expected_branch {
|
||||
return Err(anyhow!("request branch does not match expected"));
|
||||
}
|
||||
self.resolve(
|
||||
api::RunBuild { build_id },
|
||||
InnerRequestUser {
|
||||
id: String::from("github"),
|
||||
is_admin: true,
|
||||
create_server_permissions: false,
|
||||
create_build_permissions: false,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// async fn handle_procedure_webhook(
|
||||
// &self,
|
||||
// id: &str,
|
||||
// headers: HeaderMap,
|
||||
// body: String,
|
||||
// ) -> anyhow::Result<()> {
|
||||
// self.verify_gh_signature(headers, &body).await?;
|
||||
// let request_branch = extract_branch(&body)?;
|
||||
// let expected_branches = self.db.get_procedure(id).await?.webhook_branches;
|
||||
// if !expected_branches.contains(&request_branch) {
|
||||
// return Err(anyhow!("request branch does not match expected"));
|
||||
// }
|
||||
// self.run_procedure(
|
||||
// id,
|
||||
// &RequestUser {
|
||||
// id: String::from(GITHUB_WEBHOOK_USER_ID),
|
||||
// is_admin: true,
|
||||
// create_server_permissions: false,
|
||||
// create_build_permissions: false,
|
||||
// },
|
||||
// )
|
||||
// .await?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
async fn verify_gh_signature(&self, headers: HeaderMap, body: &str) -> anyhow::Result<()> {
|
||||
// wait random amount of time
|
||||
tokio::time::sleep(random_duration(0, 500)).await;
|
||||
|
||||
let signature = headers.get("x-hub-signature-256");
|
||||
if signature.is_none() {
|
||||
return Err(anyhow!("no signature in headers"));
|
||||
}
|
||||
let signature = signature.unwrap().to_str();
|
||||
if signature.is_err() {
|
||||
return Err(anyhow!("failed to unwrap signature"));
|
||||
}
|
||||
let signature = signature.unwrap().replace("sha256=", "");
|
||||
let mut mac = HmacSha256::new_from_slice(self.config.github_webhook_secret.as_bytes())
|
||||
.expect("github webhook | failed to create hmac sha256");
|
||||
mac.update(body.as_bytes());
|
||||
let expected = mac.finalize().into_bytes().encode_hex::<String>();
|
||||
if signature == expected {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("signature does not equal expected"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GithubWebhookBody {
|
||||
#[serde(rename = "ref")]
|
||||
branch: String,
|
||||
}
|
||||
|
||||
fn extract_branch(body: &str) -> anyhow::Result<String> {
|
||||
let branch = serde_json::from_str::<GithubWebhookBody>(body)
|
||||
.context("failed to parse github request body")?
|
||||
.branch
|
||||
.replace("refs/heads/", "");
|
||||
Ok(branch)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use monitor_types::{
|
||||
@@ -15,6 +15,7 @@ use monitor_types::{
|
||||
permissioned::Permissioned,
|
||||
};
|
||||
use periphery_client::{requests, PeripheryClient};
|
||||
use rand::{thread_rng, Rng};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{auth::RequestUser, state::State};
|
||||
@@ -363,3 +364,7 @@ pub fn empty_or_only_spaces(word: &str) -> bool {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn random_duration(min_ms: u64, max_ms: u64) -> Duration {
|
||||
Duration::from_millis(thread_rng().gen_range(min_ms..max_ms))
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ mod auth;
|
||||
mod cloud;
|
||||
mod config;
|
||||
mod db;
|
||||
mod github_listener;
|
||||
mod helpers;
|
||||
mod monitoring;
|
||||
mod requests;
|
||||
@@ -24,6 +25,7 @@ async fn app() -> anyhow::Result<()> {
|
||||
let app = Router::new()
|
||||
.nest("/auth", auth::router(&state))
|
||||
.nest("/api", requests::api::router())
|
||||
.nest("/listener", github_listener::router())
|
||||
.nest("/ws", ws::router())
|
||||
.layer(Extension(state));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user