2021-11-19 11:28:37 +01:00
|
|
|
use crate::WebhookeyError;
|
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;
|
|
|
|
|
|
|
|
#[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,
|
2021-11-18 17:47:13 +01:00
|
|
|
#[serde(with = "serde_regex")]
|
|
|
|
pub regex: Regex,
|
2021-11-17 15:13:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl HeaderFilter {
|
|
|
|
pub fn evaluate(&self, headers: &HeaderMap) -> Result<bool, WebhookeyError> {
|
|
|
|
trace!(
|
|
|
|
"Matching `{}` on `{}` from received header",
|
|
|
|
&self.regex,
|
|
|
|
&self.field,
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Some(value) = headers.get_one(&self.field) {
|
2021-11-18 17:47:13 +01:00
|
|
|
if self.regex.is_match(value) {
|
2021-11-17 15:13:12 +01:00
|
|
|
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,
|
2021-11-18 17:47:13 +01:00
|
|
|
#[serde(with = "serde_regex")]
|
|
|
|
pub regex: Regex,
|
2021-11-11 21:09:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl JsonFilter {
|
|
|
|
pub fn evaluate(&self, data: &serde_json::Value) -> Result<bool, WebhookeyError> {
|
|
|
|
trace!(
|
|
|
|
"Matching `{}` on `{}` from received json",
|
|
|
|
&self.regex,
|
|
|
|
&self.pointer,
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Some(value) = data.pointer(&self.pointer) {
|
2021-11-19 11:28:37 +01:00
|
|
|
if self.regex.is_match(&crate::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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-19 11:04:11 +01:00
|
|
|
macro_rules! interrelate {
|
|
|
|
($request:expr, $data:expr, $filters:expr, $relation:ident) => {{
|
|
|
|
let (mut results, mut errors) = (Vec::new(), Vec::new());
|
|
|
|
|
|
|
|
$filters
|
|
|
|
.iter()
|
|
|
|
.map(|filter| filter.evaluate($request, $data))
|
|
|
|
.for_each(|item| match item {
|
|
|
|
Ok(o) => results.push(o),
|
|
|
|
Err(e) => errors.push(e),
|
|
|
|
});
|
|
|
|
|
|
|
|
if errors.is_empty() {
|
|
|
|
Ok(results.iter().$relation(|r| *r))
|
|
|
|
} else {
|
|
|
|
errors
|
|
|
|
.iter()
|
|
|
|
.for_each(|e| error!("Could not evaluate Filter: {}", e));
|
|
|
|
|
|
|
|
Err(WebhookeyError::InvalidFilter)
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2021-11-11 21:09:47 +01:00
|
|
|
#[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-19 11:04:11 +01:00
|
|
|
FilterType::And(filters) => interrelate!(request, data, filters, all),
|
|
|
|
FilterType::Or(filters) => interrelate!(request, data, filters, any),
|
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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|