From b370d59b4073abb5e3b3b76a00ca4736ac8968dd Mon Sep 17 00:00:00 2001 From: finga Date: Fri, 19 Mar 2021 10:16:46 +0100 Subject: [PATCH] Implement secret functionality In order to validate requests a field called `secret` has to be sent containing a secret key which validates the request. A hook will be executed only if the secret sent with the request matches the hook's secret. --- README.md | 1 + config.yml | 21 +++++++++++++-------- src/main.rs | 23 +++++++++++++++++++---- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 96237d4..574f089 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Right now there is only the configuration parameter for hooks, here each hook has to be configured, It contains following fields: - action: optional string for the action to be executed when all filters match +- secrets: list of secrets - filters: list of filters Each filter has to have following fields: diff --git a/config.yml b/config.yml index ba1b687..a91c33e 100644 --- a/config.yml +++ b/config.yml @@ -1,17 +1,22 @@ --- hooks: hook1: - action: "echo hookaction1" + action: /usr/bin/local/script_xy.sh + secrets: + - secret_key_01 + - secret_key_02 filters: match_ref: - pointer: "/ref" - regex: "refs/heads/master" + pointer: /ref + regex: refs/heads/master hook2: - action: "echo hookaction2" + action: /usr/bin/local/script_xyz.sh + secrets: + - secret_key03 filters: match_ref: - pointer: "/ref" - regex: "refs/heads/master" + pointer: /ref + regex: refs/heads/master match_after: - pointer: "/after" - regex: "f6e5fe4fe37df76629112d55cc210718b6a55e7e" + pointer: /after + regex: f6e5fe4fe37df76629112d55cc210718b6a55e7e diff --git a/src/main.rs b/src/main.rs index 6422ec1..2c2e240 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ #![feature(proc_macro_hygiene, decl_macro)] use anyhow::{anyhow, bail, Result}; -use log::{debug, info, trace}; +use log::{debug, info, trace, warn}; use regex::Regex; use rocket::{fairing::AdHoc, get, post, routes, State}; use rocket_contrib::json::Json; @@ -17,6 +17,7 @@ struct Config { #[derive(Debug, Deserialize, Serialize)] struct Hook { action: Option, + secrets: Vec, filters: HashMap, } @@ -61,7 +62,7 @@ fn execute_hook(name: &str, hook: &Hook, data: &serde_json::Value) -> Result<()> if let Some(action) = &hook.action { info!("Execute `{}` from hook `{}`", action, name); - let action = action.split(" ").collect::>(); + let action = action.split(' ').collect::>(); let command = Command::new(action[0]).args(&action[1..]).output()?; @@ -91,8 +92,22 @@ fn receive_hook(address: SocketAddr, config: State, data: Json) -> trace!("Data received from: {}\n{}", address, data); - for (hook_name, hook) in config.hooks.iter() { - execute_hook(&hook_name, &hook, &data)?; + if let Some(secret) = data.pointer("secret") { + if let Some(secret) = secret.as_str() { + let hooks: HashMap<&String, &Hook> = config + .hooks + .iter() + .filter(|(_hook_name, hook)| hook.secrets.contains(&secret.to_string())) + .collect(); + + if hooks.is_empty() { + warn!("Secret did not match any hook"); + } else { + for (hook_name, hook) in hooks { + execute_hook(&hook_name, &hook, &data)?; + } + } + } } Ok("Request received.".to_string())