impl webhook route #1
+21
-8
@@ -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 =
|
||||||
|
|||||||
Reference in New Issue
Block a user