Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bin/core/src/api/listener/integrations/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Github;
impl VerifySecret for Github {
#[instrument("VerifyGithubSecret", skip_all)]
fn verify_secret(
_query: &std::collections::HashMap<String, String>,
headers: &HeaderMap,
body: &str,
custom_secret: &str,
Expand Down Expand Up @@ -53,7 +54,7 @@ struct GithubWebhookBody {
}

impl ExtractBranch for Github {
fn extract_branch(body: &str) -> anyhow::Result<String> {
fn extract_branch(_query: &std::collections::HashMap<String, String>, body: &str) -> anyhow::Result<String> {
let branch = serde_json::from_str::<GithubWebhookBody>(body)
.context("Failed to parse github request body")?
.branch
Expand Down
3 changes: 2 additions & 1 deletion bin/core/src/api/listener/integrations/gitlab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct Gitlab;
impl VerifySecret for Gitlab {
#[instrument("VerifyGitlabSecret", skip_all)]
fn verify_secret(
_query: &std::collections::HashMap<String, String>,
headers: &HeaderMap,
_body: &str,
custom_secret: &str,
Expand Down Expand Up @@ -41,7 +42,7 @@ struct GitlabWebhookBody {
}

impl ExtractBranch for Gitlab {
fn extract_branch(body: &str) -> anyhow::Result<String> {
fn extract_branch(_query: &std::collections::HashMap<String, String>, body: &str) -> anyhow::Result<String> {
let branch = serde_json::from_str::<GitlabWebhookBody>(body)
.context("Failed to parse gitlab request body")?
.branch
Expand Down
1 change: 1 addition & 0 deletions bin/core/src/api/listener/integrations/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod github;
pub mod gitlab;
pub mod query;

use super::{ExtractBranch, VerifySecret};
32 changes: 32 additions & 0 deletions bin/core/src/api/listener/integrations/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::collections::HashMap;
use axum::http::HeaderMap;
use anyhow::anyhow;

use crate::api::listener::{ExtractBranch, VerifySecret};

pub struct QueryAuth;

impl VerifySecret for QueryAuth {
fn verify_secret(
query: &HashMap<String, String>,
_headers: &HeaderMap,
_body: &str,
custom_secret: &str,
) -> anyhow::Result<()> {
if query.get("secret").map(|s| s.as_str()) == Some(custom_secret) {
Ok(())
} else {
Err(anyhow!("Invalid or missing 'secret' query parameter"))
}
}
}

impl ExtractBranch for QueryAuth {
fn extract_branch(query: &std::collections::HashMap<String, String>, _body: &str) -> anyhow::Result<String> {
if let Some(branch) = query.get("branch") {
Ok(branch.to_string())
} else {
Ok("main".to_string())
}
}
}
8 changes: 5 additions & 3 deletions bin/core/src/api/listener/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub fn router() -> Router {
Router::new()
.nest("/github", router::router::<github::Github>())
.nest("/gitlab", router::router::<gitlab::Gitlab>())
.nest("/query", router::router::<query::QueryAuth>())
}

type ListenerLockCache = CloneCache<String, Arc<Mutex<()>>>;
Expand All @@ -32,6 +33,7 @@ trait CustomSecret: KomodoResource {
/// Implemented on the integration struct, eg [integrations::github::Github]
trait VerifySecret {
fn verify_secret(
query: &std::collections::HashMap<String, String>,
headers: &HeaderMap,
body: &str,
custom_secret: &str,
Expand All @@ -40,9 +42,9 @@ trait VerifySecret {

/// Implemented on the integration struct, eg [integrations::github::Github]
trait ExtractBranch {
fn extract_branch(body: &str) -> anyhow::Result<String>;
fn verify_branch(body: &str, expected: &str) -> anyhow::Result<()> {
let branch = Self::extract_branch(body)?;
fn extract_branch(query: &std::collections::HashMap<String, String>, body: &str) -> anyhow::Result<String>;
fn verify_branch(query: &std::collections::HashMap<String, String>, body: &str, expected: &str) -> anyhow::Result<()> {
let branch = Self::extract_branch(query, body)?;
if branch == expected {
Ok(())
} else {
Expand Down
43 changes: 28 additions & 15 deletions bin/core/src/api/listener/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ fn build_locks() -> &'static ListenerLockCache {
}

pub async fn handle_build_webhook<B: super::ExtractBranch>(
query: &std::collections::HashMap<String, String>,
build: Build,
body: String,
) -> anyhow::Result<()> {
Expand All @@ -67,7 +68,7 @@ pub async fn handle_build_webhook<B: super::ExtractBranch>(
.branch
};

B::verify_branch(&body, &branch)?;
B::verify_branch(query, &body, &branch)?;

let user = git_webhook_user().to_owned();
let req = ExecuteRequest::RunBuild(RunBuild { build: build.id });
Expand Down Expand Up @@ -186,19 +187,20 @@ pub enum RepoWebhookOption {
}

pub async fn handle_repo_webhook<B: super::ExtractBranch>(
query: &std::collections::HashMap<String, String>,
option: RepoWebhookOption,
repo: Repo,
body: String,
) -> anyhow::Result<()> {
match option {
RepoWebhookOption::Clone => {
handle_repo_webhook_inner::<B, CloneRepo>(repo, body).await
handle_repo_webhook_inner::<B, CloneRepo>(query, repo, body).await
}
RepoWebhookOption::Pull => {
handle_repo_webhook_inner::<B, PullRepo>(repo, body).await
handle_repo_webhook_inner::<B, PullRepo>(query, repo, body).await
}
RepoWebhookOption::Build => {
handle_repo_webhook_inner::<B, BuildRepo>(repo, body).await
handle_repo_webhook_inner::<B, BuildRepo>(query, repo, body).await
}
}
}
Expand All @@ -207,6 +209,7 @@ async fn handle_repo_webhook_inner<
B: super::ExtractBranch,
E: RepoExecution,
>(
query: &std::collections::HashMap<String, String>,
repo: Repo,
body: String,
) -> anyhow::Result<()> {
Expand All @@ -220,7 +223,7 @@ async fn handle_repo_webhook_inner<
let lock = repo_locks().get_or_insert_default(&repo.id).await;
let _lock = lock.lock().await;

B::verify_branch(&body, &repo.config.branch)?;
B::verify_branch(query, &body, &repo.config.branch)?;

E::resolve(repo).await
}
Expand Down Expand Up @@ -308,17 +311,18 @@ pub enum StackWebhookOption {
}

pub async fn handle_stack_webhook<B: super::ExtractBranch>(
query: &std::collections::HashMap<String, String>,
option: StackWebhookOption,
stack: Stack,
body: String,
) -> anyhow::Result<()> {
match option {
StackWebhookOption::Refresh => {
handle_stack_webhook_inner::<B, RefreshStackCache>(stack, body)
handle_stack_webhook_inner::<B, RefreshStackCache>(query, stack, body)
.await
}
StackWebhookOption::Deploy => {
handle_stack_webhook_inner::<B, DeployStack>(stack, body).await
handle_stack_webhook_inner::<B, DeployStack>(query, stack, body).await
}
}
}
Expand All @@ -327,6 +331,7 @@ pub async fn handle_stack_webhook_inner<
B: super::ExtractBranch,
E: StackExecution,
>(
query: &std::collections::HashMap<String, String>,
stack: Stack,
body: String,
) -> anyhow::Result<()> {
Expand All @@ -351,7 +356,7 @@ pub async fn handle_stack_webhook_inner<
.branch
};

B::verify_branch(&body, &branch)?;
B::verify_branch(query, &body, &branch)?;

E::resolve(stack).await.map_err(|e| e.error)
}
Expand Down Expand Up @@ -419,19 +424,20 @@ pub enum SyncWebhookOption {
}

pub async fn handle_sync_webhook<B: super::ExtractBranch>(
query: &std::collections::HashMap<String, String>,
option: SyncWebhookOption,
sync: ResourceSync,
body: String,
) -> anyhow::Result<()> {
match option {
SyncWebhookOption::Refresh => {
handle_sync_webhook_inner::<B, RefreshResourceSyncPending>(
sync, body,
query, sync, body,
)
.await
}
SyncWebhookOption::Sync => {
handle_sync_webhook_inner::<B, RunSync>(sync, body).await
handle_sync_webhook_inner::<B, RunSync>(query, sync, body).await
}
}
}
Expand All @@ -440,6 +446,7 @@ async fn handle_sync_webhook_inner<
B: super::ExtractBranch,
E: SyncExecution,
>(
query: &std::collections::HashMap<String, String>,
sync: ResourceSync,
body: String,
) -> anyhow::Result<()> {
Expand All @@ -464,7 +471,7 @@ async fn handle_sync_webhook_inner<
.branch
};

B::verify_branch(&body, &branch)?;
B::verify_branch(query, &body, &branch)?;

E::resolve(sync).await
}
Expand All @@ -486,6 +493,7 @@ fn procedure_locks() -> &'static ListenerLockCache {
}

pub async fn handle_procedure_webhook<B: super::ExtractBranch>(
query: &std::collections::HashMap<String, String>,
procedure: Procedure,
target_branch: &str,
body: String,
Expand All @@ -502,7 +510,7 @@ pub async fn handle_procedure_webhook<B: super::ExtractBranch>(
let _lock = lock.lock().await;

if target_branch != ANY_BRANCH {
B::verify_branch(&body, target_branch)?;
B::verify_branch(query, &body, target_branch)?;
}

let user = git_webhook_user().to_owned();
Expand Down Expand Up @@ -540,6 +548,7 @@ fn action_locks() -> &'static ListenerLockCache {
}

pub async fn handle_action_webhook<B: super::ExtractBranch>(
query: &std::collections::HashMap<String, String>,
action: Action,
target_branch: &str,
body: String,
Expand All @@ -554,16 +563,20 @@ pub async fn handle_action_webhook<B: super::ExtractBranch>(
let lock = action_locks().get_or_insert_default(&action.id).await;
let _lock = lock.lock().await;

let branch = B::extract_branch(&body)?;
let branch = B::extract_branch(query, &body)?;

if target_branch != ANY_BRANCH && branch != target_branch {
return Err(anyhow!("request branch does not match expected"));
}

let user = git_webhook_user().to_owned();

let body = serde_json::Value::from_str(&body)
.context("Failed to deserialize webhook body")?;
let body = if body.trim().is_empty() {
serde_json::Value::Null
} else {
serde_json::Value::from_str(&body)
.context("Failed to deserialize webhook body")?
};
let serde_json::Value::Object(args) = json!({
"WEBHOOK_BRANCH": branch,
"WEBHOOK_BODY": body,
Expand Down
Loading