impl webhook route #1

Merged
qpismont merged 7 commits from webhook into main 2026-06-02 20:59:16 +02:00
Showing only changes of commit 227fcfaafb - Show all commits
+21 -8
View File
@@ -1,9 +1,11 @@
use std::fmt::Debug;
use anyhow::anyhow; use anyhow::anyhow;
use axum::body::to_bytes; use axum::body::to_bytes;
use axum::extract::FromRequest; use axum::extract::{FromRef, FromRequest, FromRequestParts, State};
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use axum::routing::{get, post}; use axum::routing::{get, post};
use axum::{Json, Router}; use axum::{Json, RequestExt, Router};
use hmac::{Hmac, KeyInit, Mac}; use hmac::{Hmac, KeyInit, Mac};
use serde_json::Value; use serde_json::Value;
use sha2::Sha256; use sha2::Sha256;
@@ -19,9 +21,9 @@ pub async fn start(app_state: AppState) -> anyhow::Result<()> {
let http_port = app_state.config.http_port; let http_port = app_state.config.http_port;
let app = Router::new() let app = Router::new()
.with_state(app_state)
.route("/", get(root)) .route("/", get(root))
.route("/webhook", post(webhook)); .route("/webhook", post(webhook))
.with_state(app_state);
let listerner = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", http_port)).await?; let listerner = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", http_port)).await?;
axum::serve(listerner, app) axum::serve(listerner, app)
@@ -45,11 +47,22 @@ pub struct WebhookExtract(pub WebhookType);
impl<S> FromRequest<S> for WebhookExtract impl<S> FromRequest<S> for WebhookExtract
where where
AppState: FromRef<S>,
S: Send + Sync, S: Send + Sync,
{ {
type Rejection = AppError; type Rejection = AppError;
async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection> { async fn from_request(
mut req: axum::extract::Request,
state: &S,
) -> Result<Self, Self::Rejection> {
let State(state) = req
.extract_parts_with_state::<State<AppState>, _>(state)
.await
.unwrap();
let secret_key = state.config.webhook_secret.as_bytes();
let headers = req.headers(); let headers = req.headers();
let sig_header = headers let sig_header = headers
.get("x-gitea-signature") .get("x-gitea-signature")
@@ -63,17 +76,17 @@ where
.await .await
.map_err(|err| anyhow!(err))?; .map_err(|err| anyhow!(err))?;
check_sig_header(secret_key, sig_header.as_bytes(), &body_bytes)?;
let Json(value) = let Json(value) =
Json::<Value>::from_bytes(&body_bytes).map_err(|_| AppError::MalformedJsonErr)?; Json::<Value>::from_bytes(&body_bytes).map_err(|_| AppError::MalformedJsonErr)?;
let webhook = WebhookType::try_from(value)?; let webhook = WebhookType::try_from(value)?;
check_sig_header(sig_header.as_bytes(), &body_bytes)?;
Ok(WebhookExtract(webhook)) Ok(WebhookExtract(webhook))
} }
} }
fn check_sig_header(sig_header: &[u8], body: &[u8]) -> Result<(), AppError> { fn check_sig_header(secret_key: &[u8], sig_header: &[u8], body: &[u8]) -> Result<(), AppError> {
let sig_header_decoded = hex::decode(sig_header).map_err(|err| anyhow!(err))?; let sig_header_decoded = hex::decode(sig_header).map_err(|err| anyhow!(err))?;
let webhook_sig_header_secret = let webhook_sig_header_secret =