Skip to content

Commit

Permalink
split of is_push
Browse files Browse the repository at this point in the history
  • Loading branch information
Jerboa-app committed Jun 3, 2024
1 parent 585d736 commit 10bec56
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 64 deletions.
9 changes: 5 additions & 4 deletions src/integrations/git/refresh.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{path::Path, sync::Arc};
use std::{path::Path, sync::Arc, time::SystemTime};

use axum::async_trait;
use chrono::{DateTime, Utc};
Expand All @@ -13,7 +13,7 @@ use super::{clean_and_clone, fast_forward_pull, HeadInfo};

pub struct GitRefreshTask
{
pub lock: Arc<Mutex<()>>,
pub lock: Arc<Mutex<SystemTime>>,
pub last_run: DateTime<Utc>,
pub next_run: Option<DateTime<Utc>>,
pub schedule: Option<Schedule>
Expand All @@ -23,7 +23,7 @@ impl GitRefreshTask
{
pub fn new
(
lock: Arc<Mutex<()>>,
lock: Arc<Mutex<SystemTime>>,
schedule: Option<Schedule>
) -> GitRefreshTask
{
Expand Down Expand Up @@ -107,9 +107,10 @@ impl Task for GitRefreshTask
{
async fn run(&mut self) -> Result<(), crate::task::TaskError>
{
let _ = self.lock.lock().await;
let mut time = self.lock.lock().await;
let config = Config::load_or_default(CONFIG_PATH);
GitRefreshTask::notify_pull(GitRefreshTask::pull(&config), &config).await;
*time = SystemTime::now();

self.schedule = schedule_from_option(config.stats.save_schedule.clone());

Expand Down
135 changes: 80 additions & 55 deletions src/integrations/github/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use axum::{body::Bytes, http::{HeaderMap, Request}, middleware::Next, response::{IntoResponse, Response}};
use std::{sync::Arc, time::SystemTime};

use axum::{body::Bytes, extract::State, http::{HeaderMap, Request}, middleware::Next, response::{IntoResponse, Response}};
use regex::Regex;
use reqwest::StatusCode;
use tokio::sync::Mutex;

use crate::config::{Config, CONFIG_PATH};

Expand All @@ -11,35 +14,98 @@ use super::git::refresh::GitRefreshTask;
/// [crate::config::GitConfig] is not None
pub async fn filter_github<B>
(
State(repo_lock): State<Arc<Mutex<SystemTime>>>,
headers: HeaderMap,
request: Request<B>,
next: Next<B>
) -> Result<Response, StatusCode>
where B: axum::body::HttpBody<Data = Bytes>
{
match is_push(&headers).await
{
StatusCode::CONTINUE => Ok(next.run(request).await),
StatusCode::OK =>
{
let body = request.into_body();
let bytes = match body.collect().await {
Ok(collected) => collected.to_bytes(),
Err(_) => {
return Ok(StatusCode::BAD_REQUEST.into_response())
}
};

let token = get_token();
if token.is_none()
{
return Ok(StatusCode::METHOD_NOT_ALLOWED.into_response());
}

match super::is_authentic
(
&headers, "x-hub-signature-256",
token.unwrap(),
&bytes
)
{
StatusCode::OK =>
{
pull(repo_lock).await;
return Ok(StatusCode::OK.into_response())
},
status => return Ok(status.into_response())
}
},
status => Ok(status.into_response())
}
}

pub fn get_token() -> Option<String>
{
let config = Config::load_or_default(CONFIG_PATH);
if config.git.is_some()
{
config.git.unwrap().remote_webhook_token
}
else
{
None
}
}

/// Perform the pull updating the mutex
async fn pull(repo_lock: Arc<Mutex<SystemTime>>)
{
let mut lock = repo_lock.lock().await;
let config = Config::load_or_default(CONFIG_PATH);
GitRefreshTask::notify_pull(GitRefreshTask::pull(&config), &config).await;
*lock = SystemTime::now();
}

/// Check if the headers conform to a github push webhook event
/// without checking it is legitimate
pub async fn is_push(headers: &HeaderMap) -> StatusCode
{
if !headers.contains_key("user-agent")
{
return StatusCode::CONTINUE
}

let user_agent = match std::str::from_utf8(headers["user-agent"].as_bytes())
{
Ok(u) => u,
Err(_) =>
{
return Ok(next.run(request).await)
return StatusCode::CONTINUE
}
};

if Regex::new(r"GitHub-Hookshot").unwrap().captures(user_agent).is_some()
{

let authentic = is_authentic(&headers, request).await;
if authentic != StatusCode::ACCEPTED
{
return Ok(authentic.into_response());
}

crate::debug("Authentic github event".to_string(), Some("GITHUB"));

if !headers.contains_key("x-github-event")
{
return Ok(StatusCode::BAD_REQUEST.into_response());
return StatusCode::BAD_REQUEST;
}

match std::str::from_utf8(headers["x-github-event"].as_bytes())
Expand All @@ -48,56 +114,15 @@ where B: axum::body::HttpBody<Data = Bytes>
{
if s.to_lowercase() == "push"
{
let config = Config::load_or_default(CONFIG_PATH);
GitRefreshTask::notify_pull(GitRefreshTask::pull(&config), &config).await;
return StatusCode::OK
}
},
}
Err(e) =>
{
crate::debug(format!("Invalid utf8 in x-github-event, {}", e), Some("GITHUB"));
return Ok(StatusCode::BAD_REQUEST.into_response());
return StatusCode::BAD_REQUEST;
}
}

return Ok(StatusCode::ACCEPTED.into_response());
}
else
{
return Ok(next.run(request).await)
}
}

async fn is_authentic<B>(headers: &HeaderMap, request: Request<B>) -> StatusCode
where B: axum::body::HttpBody<Data = Bytes>
{
let config = Config::load_or_default(CONFIG_PATH);

let token = if config.git.is_some()
{
config.git.unwrap().remote_webhook_token
}
else
{
None
};

if token.is_none()
{
return StatusCode::METHOD_NOT_ALLOWED;
}

let body = request.into_body();
let bytes = match body.collect().await {
Ok(collected) => collected.to_bytes(),
Err(_) => {
return StatusCode::BAD_REQUEST
}
};

super::is_authentic
(
&headers, "x-hub-signature-256",
token.unwrap(),
&bytes
)
StatusCode::CONTINUE
}
10 changes: 5 additions & 5 deletions src/server/https.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::
{
config::{read_config, Config, CONFIG_PATH}, content::sitemap::SiteMap, integrations::git::refresh::GitRefreshTask, server::throttle::{handle_throttle, IpThrottler}, task::{schedule_from_option, TaskPool}, CRAB
config::{read_config, Config, CONFIG_PATH}, content::sitemap::SiteMap, integrations::{git::refresh::GitRefreshTask, github::filter_github}, server::throttle::{handle_throttle, IpThrottler}, task::{schedule_from_option, TaskPool}, CRAB
};

use core::time;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::{net::{IpAddr, Ipv4Addr, SocketAddr}, time::SystemTime};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
Expand All @@ -26,7 +26,6 @@ pub struct Server
router: Router,
config: Config,
handle: Handle,
repo_mutex: Arc<Mutex<()>>,
pub tasks: TaskPool
}

Expand Down Expand Up @@ -89,15 +88,16 @@ impl Server

router = router.layer(middleware::from_fn_with_state(Some(stats.clone()), StatsDigest::filter));

let repo_mutex = Arc::new(Mutex::new(()));
let repo_mutex = Arc::new(Mutex::new(SystemTime::now()));

router = router.layer(middleware::from_fn_with_state(repo_mutex.clone(), filter_github));

let mut server = Server
{
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(a,b,c,d)), config.port_https),
router,
config: config.clone(),
handle: Handle::new(),
repo_mutex: repo_mutex.clone(),
tasks: TaskPool::new()
};

Expand Down
34 changes: 34 additions & 0 deletions tests/test_github.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
mod common;

#[cfg(test)]
mod github
{
use axum::http::{HeaderMap, HeaderValue};
use busser::integrations::github::is_push;
use reqwest::StatusCode;

#[tokio::test]
async fn test_is_push()
{
let headers = HeaderMap::new();
assert_eq!(is_push(&headers).await, StatusCode::CONTINUE);

let mut headers = HeaderMap::new();
headers.insert("user-agent", HeaderValue::from_str("not_github").unwrap());
assert_eq!(is_push(&headers).await, StatusCode::CONTINUE);

let mut headers = HeaderMap::new();
headers.insert("user-agent", HeaderValue::from_str("GitHub-Hookshot").unwrap());
assert_eq!(is_push(&headers).await, StatusCode::BAD_REQUEST);

let mut headers = HeaderMap::new();
headers.insert("user-agent", HeaderValue::from_str("GitHub-Hookshot").unwrap());
headers.insert("x-github-event", HeaderValue::from_str("not_a_push").unwrap());
assert_eq!(is_push(&headers).await, StatusCode::CONTINUE);

let mut headers = HeaderMap::new();
headers.insert("user-agent", HeaderValue::from_str("GitHub-Hookshot").unwrap());
headers.insert("x-github-event", HeaderValue::from_str("push").unwrap());
assert_eq!(is_push(&headers).await, StatusCode::OK);
}
}

0 comments on commit 10bec56

Please sign in to comment.