2021-11-11 21:09:47 +01:00
|
|
|
use anyhow::Result;
|
|
|
|
use ipnet::IpNet;
|
|
|
|
use log::{debug, error, trace};
|
|
|
|
use regex::Regex;
|
2021-11-17 15:13:12 +01:00
|
|
|
use rocket::{http::HeaderMap, Request};
|
2021-11-11 21:09:47 +01:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::net::IpAddr;
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
pub enum WebhookeyError {
|
|
|
|
#[error("Could not extract signature from header")]
|
|
|
|
InvalidSignature,
|
|
|
|
#[error("Unauthorized request from `{0}`")]
|
|
|
|
Unauthorized(IpAddr),
|
|
|
|
#[error("Unmatched hook from `{0}`")]
|
|
|
|
UnmatchedHook(IpAddr),
|
|
|
|
#[error("Could not evaluate filter request")]
|
|
|
|
InvalidFilter,
|
|
|
|
#[error("IO Error")]
|
|
|
|
Io(std::io::Error),
|
|
|
|
#[error("Serde Error")]
|
|
|
|
Serde(serde_json::Error),
|
|
|
|
#[error("Regex Error")]
|
|
|
|
Regex(regex::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
#[serde(deny_unknown_fields, untagged)]
|
|
|
|
pub enum AddrType {
|
|
|
|
IpAddr(IpAddr),
|
|
|
|
IpNet(IpNet),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AddrType {
|
|
|
|
pub 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")]
|
|
|
|
pub enum IpFilter {
|
|
|
|
Allow(Vec<AddrType>),
|
|
|
|
Deny(Vec<AddrType>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IpFilter {
|
|
|
|
pub 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)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-17 15:13:12 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct HeaderFilter {
|
|
|
|
pub field: String,
|
|
|
|
pub regex: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HeaderFilter {
|
|
|
|
pub fn evaluate(&self, headers: &HeaderMap) -> Result<bool, WebhookeyError> {
|
|
|
|
trace!(
|
|
|
|
"Matching `{}` on `{}` from received header",
|
|
|
|
&self.regex,
|
|
|
|
&self.field,
|
|
|
|
);
|
|
|
|
|
|
|
|
let regex = Regex::new(&self.regex).map_err(WebhookeyError::Regex)?;
|
|
|
|
|
|
|
|
if let Some(value) = headers.get_one(&self.field) {
|
|
|
|
if regex.is_match(value) {
|
|
|
|
debug!("Regex `{}` for `{}` matches", &self.regex, &self.field);
|
|
|
|
|
|
|
|
return Ok(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!(
|
|
|
|
"Regex `{}` for header field `{}` does not match",
|
|
|
|
&self.regex, &self.field
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 21:09:47 +01:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct JsonFilter {
|
|
|
|
pub pointer: String,
|
|
|
|
pub regex: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl JsonFilter {
|
|
|
|
pub fn evaluate(&self, data: &serde_json::Value) -> Result<bool, WebhookeyError> {
|
|
|
|
trace!(
|
|
|
|
"Matching `{}` on `{}` from received json",
|
|
|
|
&self.regex,
|
|
|
|
&self.pointer,
|
|
|
|
);
|
|
|
|
|
|
|
|
let regex = Regex::new(&self.regex).map_err(WebhookeyError::Regex)?;
|
|
|
|
|
|
|
|
if let Some(value) = data.pointer(&self.pointer) {
|
2021-11-16 14:39:22 +01:00
|
|
|
if regex.is_match(&get_string(value)?) {
|
2021-11-11 21:09:47 +01:00
|
|
|
debug!("Regex `{}` for `{}` matches", &self.regex, &self.pointer);
|
|
|
|
|
|
|
|
return Ok(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!(
|
2021-11-17 15:13:12 +01:00
|
|
|
"Regex `{}` for json field `{}` does not match",
|
2021-11-11 21:09:47 +01:00
|
|
|
&self.regex, &self.pointer
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
#[serde(deny_unknown_fields, rename_all = "lowercase")]
|
|
|
|
pub enum FilterType {
|
2021-11-17 14:06:07 +01:00
|
|
|
Not(Box<FilterType>),
|
2021-11-11 21:09:47 +01:00
|
|
|
And(Vec<FilterType>),
|
|
|
|
Or(Vec<FilterType>),
|
2021-11-17 15:13:12 +01:00
|
|
|
#[serde(rename = "header")]
|
|
|
|
HeaderFilter(HeaderFilter),
|
2021-11-11 21:09:47 +01:00
|
|
|
#[serde(rename = "json")]
|
|
|
|
JsonFilter(JsonFilter),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FilterType {
|
2021-11-17 15:13:12 +01:00
|
|
|
pub fn evaluate(
|
|
|
|
&self,
|
|
|
|
request: &Request,
|
|
|
|
data: &serde_json::Value,
|
|
|
|
) -> Result<bool, WebhookeyError> {
|
2021-11-11 21:09:47 +01:00
|
|
|
match self {
|
2021-11-17 15:13:12 +01:00
|
|
|
FilterType::Not(filter) => Ok(!filter.evaluate(request, data)?),
|
2021-11-11 21:09:47 +01:00
|
|
|
FilterType::And(filters) => {
|
2021-11-13 14:16:21 +01:00
|
|
|
let (mut results, mut errors) = (Vec::new(), Vec::new());
|
|
|
|
|
|
|
|
filters
|
2021-11-11 21:09:47 +01:00
|
|
|
.iter()
|
2021-11-17 15:13:12 +01:00
|
|
|
.map(|filter| filter.evaluate(request, data))
|
2021-11-13 14:16:21 +01:00
|
|
|
.for_each(|item| match item {
|
|
|
|
Ok(o) => results.push(o),
|
|
|
|
Err(e) => errors.push(e),
|
|
|
|
});
|
2021-11-11 21:09:47 +01:00
|
|
|
|
|
|
|
if errors.is_empty() {
|
2021-11-13 14:16:21 +01:00
|
|
|
Ok(results.iter().all(|r| *r))
|
2021-11-11 21:09:47 +01:00
|
|
|
} else {
|
2021-11-13 14:16:21 +01:00
|
|
|
errors
|
|
|
|
.iter()
|
|
|
|
.for_each(|e| error!("Could not evaluate Filter: {}", e));
|
2021-11-11 21:09:47 +01:00
|
|
|
|
|
|
|
Err(WebhookeyError::InvalidFilter)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FilterType::Or(filters) => {
|
2021-11-13 14:16:21 +01:00
|
|
|
let (mut results, mut errors) = (Vec::new(), Vec::new());
|
|
|
|
|
|
|
|
filters
|
2021-11-11 21:09:47 +01:00
|
|
|
.iter()
|
2021-11-17 15:13:12 +01:00
|
|
|
.map(|filter| filter.evaluate(request, data))
|
2021-11-13 14:16:21 +01:00
|
|
|
.for_each(|item| match item {
|
|
|
|
Ok(o) => results.push(o),
|
|
|
|
Err(e) => errors.push(e),
|
|
|
|
});
|
2021-11-11 21:09:47 +01:00
|
|
|
|
|
|
|
if errors.is_empty() {
|
2021-11-13 14:16:21 +01:00
|
|
|
Ok(results.iter().any(|r| *r))
|
2021-11-11 21:09:47 +01:00
|
|
|
} else {
|
2021-11-13 14:16:21 +01:00
|
|
|
errors
|
|
|
|
.iter()
|
|
|
|
.for_each(|e| error!("Could not evaluate Filter: {}", e));
|
2021-11-11 21:09:47 +01:00
|
|
|
|
|
|
|
Err(WebhookeyError::InvalidFilter)
|
|
|
|
}
|
|
|
|
}
|
2021-11-17 15:13:12 +01:00
|
|
|
FilterType::HeaderFilter(filter) => filter.evaluate(request.headers()),
|
2021-11-11 21:09:47 +01:00
|
|
|
FilterType::JsonFilter(filter) => filter.evaluate(data),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-16 14:39:22 +01:00
|
|
|
|
|
|
|
pub fn get_string(data: &serde_json::Value) -> Result<String, WebhookeyError> {
|
|
|
|
match &data {
|
|
|
|
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()),
|
|
|
|
x => {
|
|
|
|
error!("Could not get string from: {:?}", x);
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|