Update build deps and improve readability

The ip filtering is improved as well as the replacing of
parameters. The index page is removed.
This commit is contained in:
finga 2021-05-29 00:50:48 +02:00
parent 29caaff596
commit 891a8a70ae
4 changed files with 204 additions and 280 deletions

202
Cargo.lock generated
View file

@ -58,9 +58,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
version = "0.7.15"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
@ -71,12 +71,6 @@ version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.2"
@ -110,12 +104,6 @@ dependencies = [
"safemem",
]
[[package]]
name = "base64"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
[[package]]
name = "base64"
version = "0.13.0"
@ -140,17 +128,6 @@ dependencies = [
"wyz",
]
[[package]]
name = "blake2b_simd"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.9.0"
@ -168,9 +145,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.67"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
[[package]]
name = "cfg-if"
@ -187,12 +164,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "cookie"
version = "0.11.4"
@ -210,10 +181,13 @@ dependencies = [
]
[[package]]
name = "cpuid-bool"
version = "0.1.2"
name = "cpufeatures"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8"
dependencies = [
"libc",
]
[[package]]
name = "cpuid-bool"
@ -221,17 +195,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
[[package]]
name = "crossbeam-utils"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
dependencies = [
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]]
name = "crypto-mac"
version = "0.10.0"
@ -294,18 +257,18 @@ dependencies = [
[[package]]
name = "dirs"
version = "3.0.1"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff"
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
"libc",
"redox_users",
@ -359,24 +322,13 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.1.16"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"wasi",
]
[[package]]
@ -438,9 +390,9 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437"
checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68"
[[package]]
name = "humantime"
@ -542,9 +494,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.93"
version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
[[package]]
name = "linked-hash-map"
@ -578,9 +530,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "memchr"
version = "2.3.4"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "mime"
@ -660,7 +612,7 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"
dependencies = [
"cpuid-bool 0.2.0",
"cpuid-bool",
"opaque-debug",
"universal-hash",
]
@ -682,11 +634,11 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.26"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
"unicode-xid 0.2.1",
"unicode-xid 0.2.2",
]
[[package]]
@ -704,7 +656,7 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2 1.0.26",
"proc-macro2 1.0.27",
]
[[package]]
@ -741,7 +693,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom 0.2.2",
"getrandom",
]
[[package]]
@ -755,26 +707,28 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.1.57"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.3.5"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom 0.1.16",
"getrandom",
"redox_syscall",
"rust-argon2",
]
[[package]]
name = "regex"
version = "1.4.5"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
@ -783,9 +737,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.23"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ring"
@ -801,12 +755,12 @@ dependencies = [
[[package]]
name = "rocket"
version = "0.4.7"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7febfdfd4d43facfc7daba20349ebe2c310c6735bd6a2a9255ea8bc425b4cb13"
checksum = "4a7ab1dfdc75bb8bd2be381f37796b1b300c45a3c9145b34d86715e8dd90bf28"
dependencies = [
"atty",
"base64 0.12.3",
"base64 0.13.0",
"log 0.4.14",
"memchr",
"num_cpus",
@ -822,9 +776,9 @@ dependencies = [
[[package]]
name = "rocket_codegen"
version = "0.4.7"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceac2c55b2c8b1cdc53add64332defa5fc227f64263b86b4114d1386286d42a3"
checksum = "1729e687d6d2cf434d174da84fb948f7fef4fac22d20ce94ca61c28b72dbcf9f"
dependencies = [
"devise",
"glob",
@ -837,9 +791,9 @@ dependencies = [
[[package]]
name = "rocket_http"
version = "0.4.7"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce364100ed7a1bf39257b69ebd014c1d5b4979b0d365d8c9ab0aa9c79645493d"
checksum = "6131e6e6d38a9817f4a494ff5da95971451c2eb56a53915579fc9c80f6ef0117"
dependencies = [
"cookie",
"hyper",
@ -863,18 +817,6 @@ dependencies = [
"fsio",
]
[[package]]
name = "rust-argon2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
dependencies = [
"base64 0.13.0",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]]
name = "rustls"
version = "0.14.0"
@ -913,22 +855,22 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.125"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.125"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
dependencies = [
"proc-macro2 1.0.26",
"proc-macro2 1.0.27",
"quote 1.0.9",
"syn 1.0.70",
"syn 1.0.72",
]
[[package]]
@ -956,13 +898,13 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.9.3"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
dependencies = [
"block-buffer",
"cfg-if",
"cpuid-bool 0.1.2",
"cpufeatures",
"digest",
"opaque-debug",
]
@ -1004,13 +946,13 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.70"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
dependencies = [
"proc-macro2 1.0.26",
"proc-macro2 1.0.27",
"quote 1.0.9",
"unicode-xid 0.2.1",
"unicode-xid 0.2.2",
]
[[package]]
@ -1030,22 +972,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.24"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.24"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
dependencies = [
"proc-macro2 1.0.26",
"proc-macro2 1.0.27",
"quote 1.0.9",
"syn 1.0.70",
"syn 1.0.72",
]
[[package]]
@ -1135,9 +1077,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "universal-hash"
@ -1188,12 +1130,6 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"

View file

@ -29,7 +29,7 @@ thiserror = "1.0"
run_script = "0.7"
[package.metadata.deb]
extended-description = "Webhookey receives requests as for example sent by Gitea's webhooks. Those requests are filtered against configurable filters. When a filter matches values from the header and the body can be passed to scripts which are then executed."
extended-description = "Webhookey receives requests in form of a so called Webhook as for example sent by Gitea. Those requests are matched against configured filters, if a filter matches, values from the header and the body can be passed to scripts as parameters which are then executed subsequently."
maintainer-scripts = "debian/"
systemd-units = { enable = false }
assets = [

View file

@ -44,7 +44,7 @@ Webhookey can either be run from the project directory with:
```
or you can copy the produced binary somewhere else from
`webhookey/target/{debug, release}/webhookey` depending on which one
`webhookey/target/{debug,release}/webhookey` depending on which one
you built.
## Configuration

View file

@ -16,7 +16,6 @@ use regex::Regex;
use rocket::{
data::{self, FromDataSimple},
fairing::AdHoc,
get,
http::{HeaderMap, Status},
post, routes, Data,
Outcome::{Failure, Success},
@ -41,6 +40,15 @@ enum AddrType {
IpNet(IpNet),
}
impl AddrType {
fn matches(&self, client_ip: &IpAddr) -> bool {
match self {
AddrType::IpAddr(addr) => addr == client_ip,
AddrType::IpNet(net) => net.contains(client_ip),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields, rename_all = "lowercase")]
enum IpFilter {
@ -48,6 +56,15 @@ enum IpFilter {
Deny(Vec<AddrType>),
}
impl IpFilter {
fn validate(&self, client_ip: &IpAddr) -> bool {
match self {
IpFilter::Allow(list) => list.iter().any(|i| i.matches(client_ip)),
IpFilter::Deny(list) => !list.iter().any(|i| i.matches(client_ip)),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
struct Config {
@ -74,7 +91,7 @@ struct Filter {
#[derive(Debug, Error)]
enum WebhookeyError {
#[error("Could not extract signature from header")]
InvalidHeader,
InvalidSignature,
#[error("Unauthorized request from `{0}`")]
Unauthorized(IpAddr),
#[error("Unmatched hook from `{0}`")]
@ -86,58 +103,29 @@ enum WebhookeyError {
}
#[derive(Debug)]
struct Hooks(HashMap<String, String>);
struct Hooks {
inner: HashMap<String, String>,
}
fn accept_ip(hook_name: &str, client_ip: &IpAddr, ip_filter: &Option<IpFilter>) -> bool {
match ip_filter {
Some(IpFilter::Allow(list)) => {
for i in list {
match i {
AddrType::IpAddr(addr) => {
if addr == client_ip {
info!("Allow hook `{}` from {}", &hook_name, &addr);
return true;
}
}
AddrType::IpNet(net) => {
if net.contains(client_ip) {
info!("Allow hook `{}` from {}", &hook_name, &net);
return true;
}
}
}
Some(filter) => {
if filter.validate(client_ip) {
info!("Allow hook `{}` from {}", &hook_name, &client_ip);
true
} else {
warn!("Deny hook `{}` from {}", &hook_name, &client_ip);
false
}
warn!("Deny hook `{}` from {}", &hook_name, &client_ip);
return false;
}
Some(IpFilter::Deny(list)) => {
for i in list {
match i {
AddrType::IpAddr(addr) => {
if addr == client_ip {
warn!("Deny hook `{}` from {}", &hook_name, &addr);
return false;
}
}
AddrType::IpNet(net) => {
if net.contains(client_ip) {
warn!("Deny hook `{}` from {}", &hook_name, &net);
return false;
}
}
}
}
info!("Allow hook `{}` from {}", &hook_name, &client_ip)
None => {
info!(
"Allow hook `{}` from {}, no IP filter was configured",
&hook_name, &client_ip
);
true
}
None => info!(
"Allow hook `{}` from {} as no IP filter was configured",
&hook_name, &client_ip
),
}
true
}
fn validate_request(secret: &str, signature: &str, data: &[u8]) -> Result<()> {
@ -161,7 +149,31 @@ fn get_parameter(input: &str) -> Result<Vec<&str>> {
Ok(result)
}
fn replace_parameter(input: &str, headers: &HeaderMap, data: &serde_json::Value) -> Result<String> {
fn get_header_field<'a>(headers: &'a HeaderMap, param: &[&str]) -> Result<&'a str> {
headers
.get_one(
param
.get(1)
.ok_or_else(|| anyhow!("Missing parameter for `header` expression"))?,
)
.ok_or_else(|| anyhow!("Could not extract event parameter from header"))
}
fn get_value_from_pointer<'a>(data: &'a serde_json::Value, pointer: &'a str) -> Result<&'a str> {
let value = data
.pointer(pointer)
.ok_or_else(|| anyhow!("Could not get field from pointer {}", pointer))?;
value
.as_str()
.ok_or_else(|| anyhow!("Could not convert value `{}` to string", value))
}
fn replace_parameters(
input: &str,
headers: &HeaderMap,
data: &serde_json::Value,
) -> Result<String> {
let parse: IResult<&str, Vec<&str>> = many0(alt((
map_res(
delimited(tag("{{"), take_until("}}"), tag("}}")),
@ -169,23 +181,8 @@ fn replace_parameter(input: &str, headers: &HeaderMap, data: &serde_json::Value)
let expr = param.trim().split(' ').collect::<Vec<&str>>();
match expr.get(0) {
Some(&"header") => {
if let Some(field) = expr.get(1) {
match headers.get_one(field) {
Some(value) => Ok(value),
_ => bail!("Could not extract event parameter from header"),
}
} else {
bail!("Missing parameter for `header` expression");
}
}
Some(pointer) => match data.pointer(pointer) {
Some(value) => match value.as_str() {
Some(value) => Ok(value),
_ => bail!("Could not convert value `{}` to string", value),
},
_ => bail!("Could not convert field `{}` to string", param.trim()),
},
Some(&"header") => get_header_field(headers, &expr),
Some(pointer) => get_value_from_pointer(data, &pointer),
None => bail!("Missing expression in `{}`", input),
}
},
@ -203,12 +200,13 @@ fn replace_parameter(input: &str, headers: &HeaderMap, data: &serde_json::Value)
fn get_string(value: &serde_json::Value) -> Result<String> {
match &value {
serde_json::Value::Null => unimplemented!(),
serde_json::Value::Bool(bool) => Ok(bool.to_string()),
serde_json::Value::Number(number) => Ok(number.to_string()),
serde_json::Value::String(string) => Ok(string.as_str().to_string()),
serde_json::Value::Array(_array) => unimplemented!(),
serde_json::Value::Object(_object) => unimplemented!(),
x => {
error!("Could not get string from: {:?}", x);
unimplemented!()
}
}
}
@ -228,15 +226,8 @@ fn filter_match(
let parameter = parameter.trim();
if let Some(json_value) = data.pointer(parameter) {
*data.pointer_mut(parameter).unwrap() = match json_value {
serde_json::Value::Bool(bool) => serde_json::Value::String(bool.to_string()),
serde_json::Value::String(string) => serde_json::Value::String(string.to_string()),
serde_json::Value::Number(number) => serde_json::Value::String(number.to_string()),
x => {
error!("Could not get string from: {:?}", x);
unimplemented!()
}
}
*data.pointer_mut(parameter).unwrap() =
serde_json::Value::String(get_string(json_value)?);
}
}
@ -246,7 +237,7 @@ fn filter_match(
if regex.is_match(&value) {
debug!("Filter `{}` of hook `{}` matched", filter_name, hook_name);
return Ok(Some(replace_parameter(
return Ok(Some(replace_parameters(
&hook.command,
&request.headers(),
data,
@ -272,47 +263,56 @@ fn execute_hooks(request: &Request, data: Data) -> Result<Hooks, WebhookeyError>
let config = request.guard::<State<Config>>().unwrap(); // should never fail
let mut valid = false;
let mut hooks = HashMap::new();
let mut result = HashMap::new();
let client_ip = &request
.client_ip()
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
for (hook_name, hook) in &config.hooks {
if accept_ip(&hook_name, &client_ip, &hook.ip_filter) {
if let Some(signature) = request.headers().get_one(&hook.signature) {
for secret in &hook.secrets {
match validate_request(&secret, &signature, &buffer) {
Ok(()) => {
trace!("Valid signature found for hook `{}`", hook_name,);
let hooks = config
.hooks
.iter()
.filter(|(name, hook)| accept_ip(&name, &client_ip, &hook.ip_filter));
valid = true;
for (hook_name, hook) in hooks {
let signature = request
.headers()
.get_one(&hook.signature)
.ok_or_else(|| WebhookeyError::InvalidSignature)?;
let mut data: serde_json::Value =
serde_json::from_slice(&buffer).map_err(WebhookeyError::Serde)?;
let secrets = hook
.secrets
.iter()
.map(|secret| validate_request(&secret, &signature, &buffer));
for (filter_name, filter) in &hook.filters {
match filter_match(
&hook_name,
&hook,
&filter_name,
&filter,
&request,
&mut data,
) {
Ok(Some(command)) => {
hooks.insert(hook_name.to_string(), command);
break;
}
Ok(None) => {}
Err(e) => error!("{}", e),
}
for secret in secrets {
match secret {
Ok(()) => {
trace!("Valid signature found for hook `{}`", hook_name);
valid = true;
let mut data: serde_json::Value =
serde_json::from_slice(&buffer).map_err(WebhookeyError::Serde)?;
for (filter_name, filter) in &hook.filters {
match filter_match(
&hook_name,
&hook,
&filter_name,
&filter,
&request,
&mut data,
) {
Ok(Some(command)) => {
result.insert(hook_name.to_string(), command);
break;
}
Ok(None) => {}
Err(e) => error!("{}", e),
}
Err(e) => trace!("Hook `{}` could not validate request: {}", &hook_name, e),
}
}
} else {
return Err(WebhookeyError::InvalidHeader);
Err(e) => trace!("Hook `{}` could not validate request: {}", &hook_name, e),
}
}
}
@ -321,7 +321,7 @@ fn execute_hooks(request: &Request, data: Data) -> Result<Hooks, WebhookeyError>
return Err(WebhookeyError::Unauthorized(*client_ip));
}
Ok(Hooks(hooks))
Ok(Hooks { inner: result })
}
impl FromDataSimple for Hooks {
@ -330,7 +330,7 @@ impl FromDataSimple for Hooks {
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, Self::Error> {
match execute_hooks(&request, data) {
Ok(hooks) => {
if hooks.0.is_empty() {
if hooks.inner.is_empty() {
let client_ip = &request
.client_ip()
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
@ -353,26 +353,21 @@ impl FromDataSimple for Hooks {
}
}
#[get("/")]
fn index() -> &'static str {
"Hello, webhookey!"
}
#[post("/", format = "json", data = "<hooks>")]
fn receive_hook<'a>(address: SocketAddr, hooks: Hooks) -> Result<Response<'a>> {
info!("Post request received from: {}", address);
for hook in hooks.0 {
info!("Execute `{}` from hook `{}`", &hook.1, &hook.0);
for (name, command) in hooks.inner {
info!("Execute `{}` from hook `{}`", &command, &name);
match run_script::run(&hook.1, &vec![], &ScriptOptions::new()) {
match run_script::run(&command, &vec![], &ScriptOptions::new()) {
Ok((status, stdout, stderr)) => {
info!("Command `{}` exited with return code: {}", &hook.1, status);
trace!("Output of command `{}` on stdout: {:?}", &hook.1, &stdout);
debug!("Output of command `{}` on stderr: {:?}", &hook.1, &stderr);
info!("Command `{}` exited with return code: {}", &command, status);
trace!("Output of command `{}` on stdout: {:?}", &command, &stdout);
debug!("Output of command `{}` on stderr: {:?}", &command, &stderr);
}
Err(e) => {
error!("Execution of `{}` failed: {}", &hook.1, e);
error!("Execution of `{}` failed: {}", &command, e);
}
}
}
@ -381,12 +376,14 @@ fn receive_hook<'a>(address: SocketAddr, hooks: Hooks) -> Result<Response<'a>> {
}
fn get_config() -> Result<File> {
// Look for systemwide config..
if let Ok(config) = File::open("/etc/webhookey/config.yml") {
info!("Loading configuration from `/etc/webhookey/config.yml`");
return Ok(config);
}
// ..look for user path config..
if let Some(mut path) = dirs::config_dir() {
path.push("webhookey/config.yml");
@ -400,12 +397,14 @@ fn get_config() -> Result<File> {
}
}
// ..look for config in CWD..
if let Ok(config) = File::open("config.yml") {
info!("Loading configuration from `./config.yml`");
return Ok(config);
}
// ..you had your chance.
bail!("No configuration file found.");
}
@ -417,7 +416,7 @@ fn main() -> Result<()> {
trace!("Parsed configuration:\n{}", serde_yaml::to_string(&config)?);
rocket::ignite()
.mount("/", routes![index, receive_hook])
.mount("/", routes![receive_hook])
.attach(AdHoc::on_attach("webhookey config", move |rocket| {
Ok(rocket.manage(config))
}))
@ -435,17 +434,6 @@ mod tests {
};
use serde_json::json;
#[test]
fn index() {
let rocket = rocket::ignite().mount("/", routes![index]);
let client = Client::new(rocket).unwrap();
let mut response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string(), Some("Hello, webhookey!".into()));
}
#[test]
fn secret() {
let mut hooks = HashMap::new();
@ -520,42 +508,42 @@ mod tests {
map.add_raw("X-Gitea-Event", "something");
assert_eq!(
replace_parameter("command", &map, &serde_json::Value::Null).unwrap(),
replace_parameters("command", &map, &serde_json::Value::Null).unwrap(),
"command"
);
assert_eq!(
replace_parameter(" command", &map, &serde_json::Value::Null).unwrap(),
replace_parameters(" command", &map, &serde_json::Value::Null).unwrap(),
" command"
);
assert_eq!(
replace_parameter("command ", &map, &serde_json::Value::Null).unwrap(),
replace_parameters("command ", &map, &serde_json::Value::Null).unwrap(),
"command "
);
assert_eq!(
replace_parameter(" command ", &map, &serde_json::Value::Null).unwrap(),
replace_parameters(" command ", &map, &serde_json::Value::Null).unwrap(),
" command "
);
assert_eq!(
replace_parameter("command command ", &map, &serde_json::Value::Null).unwrap(),
replace_parameters("command command ", &map, &serde_json::Value::Null).unwrap(),
"command command "
);
assert_eq!(
replace_parameter("{{ /foo }} command", &map, &json!({ "foo": "bar" })).unwrap(),
replace_parameters("{{ /foo }} command", &map, &json!({ "foo": "bar" })).unwrap(),
"bar command"
);
assert_eq!(
replace_parameter(" command {{ /foo }} ", &map, &json!({ "foo": "bar" })).unwrap(),
replace_parameters(" command {{ /foo }} ", &map, &json!({ "foo": "bar" })).unwrap(),
" command bar "
);
assert_eq!(
replace_parameter(
replace_parameters(
"{{ /foo }} command{{/field1/foo}}",
&map,
&json!({ "foo": "bar", "field1": { "foo": "baz" } })
@ -565,12 +553,12 @@ mod tests {
);
assert_eq!(
replace_parameter(" command {{ /foo }} ", &map, &json!({ "foo": "bar" })).unwrap(),
replace_parameters(" command {{ /foo }} ", &map, &json!({ "foo": "bar" })).unwrap(),
" command bar "
);
assert_eq!(
replace_parameter(
replace_parameters(
" {{ /field1/foo }} command",
&map,
&json!({ "field1": { "foo": "bar" } })
@ -580,7 +568,7 @@ mod tests {
);
assert_eq!(
replace_parameter(
replace_parameters(
" {{ header X-Gitea-Event }} command",
&map,
&json!({ "field1": { "foo": "bar" } })