205 lines
6.3 KiB
Rust
205 lines
6.3 KiB
Rust
/*
|
|
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
use actix_web::web;
|
|
use actix_web::HttpRequest;
|
|
use hmac::{Hmac, Mac};
|
|
use serde::{Deserialize, Serialize};
|
|
use sha2::Sha256;
|
|
use tracing::{info, warn};
|
|
use url::Url;
|
|
|
|
use crate::ctx::Ctx;
|
|
use crate::errors::ServiceError;
|
|
use crate::errors::ServiceResult;
|
|
|
|
pub type HmacSha256 = Hmac<Sha256>;
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
|
|
pub struct CommitPerson {
|
|
pub name: String,
|
|
pub email: String,
|
|
pub username: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, Eq, PartialEq)]
|
|
pub struct Commit {
|
|
pub id: String,
|
|
pub message: String,
|
|
pub url: String,
|
|
pub author: CommitPerson,
|
|
pub committer: CommitPerson,
|
|
pub verification: serde_json::Value,
|
|
pub timestamp: String,
|
|
pub added: serde_json::Value,
|
|
pub removed: serde_json::Value,
|
|
pub modified: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
|
|
pub struct Person {
|
|
pub id: usize,
|
|
pub login: String,
|
|
pub full_name: String,
|
|
pub email: String,
|
|
pub avatar_url: String,
|
|
pub language: String,
|
|
pub is_admin: bool,
|
|
pub last_login: String,
|
|
pub created: String,
|
|
pub restricted: bool,
|
|
pub active: bool,
|
|
pub prohibit_login: bool,
|
|
pub location: String,
|
|
pub website: String,
|
|
pub description: String,
|
|
pub visibility: String,
|
|
pub followers_count: usize,
|
|
pub following_count: usize,
|
|
pub starred_repos_count: usize,
|
|
pub username: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
|
|
pub struct Permissions {
|
|
pub admin: bool,
|
|
pub push: bool,
|
|
pub pull: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
|
|
pub struct InternalTracker {
|
|
pub enable_time_tracker: bool,
|
|
pub allow_only_contributors_to_track_time: bool,
|
|
pub enable_issue_dependencies: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
|
|
pub struct Repository {
|
|
pub id: usize,
|
|
pub owner: Person,
|
|
pub name: String,
|
|
pub full_name: String,
|
|
pub description: String,
|
|
pub empty: bool,
|
|
pub private: bool,
|
|
pub fork: bool,
|
|
pub template: bool,
|
|
pub parent: Option<serde_json::Value>,
|
|
pub mirror: bool,
|
|
pub size: usize,
|
|
pub html_url: String,
|
|
pub ssh_url: String,
|
|
pub clone_url: String,
|
|
pub original_url: String,
|
|
pub website: String,
|
|
pub stars_count: usize,
|
|
pub forks_count: usize,
|
|
pub watchers_count: usize,
|
|
pub open_issues_count: usize,
|
|
pub open_pr_counter: usize,
|
|
pub release_counter: usize,
|
|
pub default_branch: String,
|
|
pub archived: bool,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
pub permissions: Permissions,
|
|
pub has_issues: bool,
|
|
pub internal_tracker: InternalTracker,
|
|
pub has_wiki: bool,
|
|
pub has_pull_requests: bool,
|
|
pub has_projects: bool,
|
|
pub ignore_whitespace_conflicts: bool,
|
|
pub allow_merge_commits: bool,
|
|
pub allow_rebase: bool,
|
|
pub allow_rebase_explicit: bool,
|
|
pub allow_squash_merge: bool,
|
|
pub default_merge_style: String,
|
|
pub avatar_url: String,
|
|
pub internal: bool,
|
|
pub mirror_interval: String,
|
|
pub mirror_updated: String,
|
|
pub repo_transfer: Option<serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
|
|
pub struct WebhookPayload {
|
|
#[serde(rename(serialize = "ref", deserialize = "ref"))]
|
|
pub reference: String,
|
|
pub before: String,
|
|
pub after: String,
|
|
pub compare_url: String,
|
|
pub repository: Repository,
|
|
pub pusher: Person,
|
|
pub sender: Person,
|
|
}
|
|
|
|
impl Ctx {
|
|
pub async fn process_webhook(
|
|
&self,
|
|
body: &web::Bytes,
|
|
req: &HttpRequest,
|
|
auth_token: &str,
|
|
) -> ServiceResult<()> {
|
|
let headers = req.headers();
|
|
let _uuid = headers.get("X-Gitea-Delivery").unwrap();
|
|
let sig = headers.get("X-Gitea-Signature").unwrap();
|
|
let sig = hex::decode(sig).unwrap();
|
|
let event_type = headers.get("X-Gitea-Event").unwrap();
|
|
|
|
let payload: WebhookPayload = serde_json::from_slice(&body).unwrap();
|
|
|
|
let hook = self.db.get_webhook(auth_token).await?;
|
|
|
|
for url in [
|
|
&payload.repository.html_url,
|
|
&payload.repository.ssh_url,
|
|
&payload.repository.clone_url,
|
|
] {
|
|
if self.db.site_with_repository_exists(url).await? {
|
|
let mut mac = HmacSha256::new_from_slice(hook.forgejo_webhook_secret.as_bytes())?;
|
|
mac.update(&body);
|
|
mac.verify_slice(&sig[..])?;
|
|
|
|
let site = self.db.get_site_from_repo_url(url).await?;
|
|
self.db
|
|
.webhook_link_site(auth_token, &Url::parse(&site.repo_url)?)
|
|
.await?;
|
|
if payload.reference.contains(&site.branch) {
|
|
info!(
|
|
"[webhook][forgejo/gitea] received update {:?} from {url} repository on deployed branch",
|
|
event_type
|
|
);
|
|
self.update_site(&site.site_secret, Some(site.branch))
|
|
.await?;
|
|
} else {
|
|
info!(
|
|
"[webhook][forgejo/gitea] received update {:?} from {url} repository on non-deployed branch {}",
|
|
event_type,
|
|
payload.reference
|
|
);
|
|
}
|
|
return Ok(());
|
|
}
|
|
}
|
|
warn!(
|
|
"[webhook][forgejo/gitea] stray update from {} repository",
|
|
payload.repository.html_url
|
|
);
|
|
Err(ServiceError::WebsiteNotFound)
|
|
}
|
|
}
|