Skip to main content

harn_vm/connectors/webhook/
variants.rs

1use std::collections::BTreeMap;
2
3use futures::executor::block_on;
4use serde::{Deserialize, Serialize};
5use time::{Duration, OffsetDateTime};
6
7use crate::connectors::hmac::{verify_hmac_signed, HmacSignatureStyle};
8use crate::connectors::ConnectorError;
9use crate::event_log::AnyEventLog;
10use crate::triggers::ProviderId;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum WebhookSignatureVariant {
15    Standard,
16    Stripe,
17    GitHub,
18    Slack,
19}
20
21impl WebhookSignatureVariant {
22    pub fn parse(raw: Option<&str>) -> Result<Self, ConnectorError> {
23        match raw.unwrap_or("standard") {
24            "standard" => Ok(Self::Standard),
25            "stripe" => Ok(Self::Stripe),
26            "github" => Ok(Self::GitHub),
27            "slack" => Ok(Self::Slack),
28            other => Err(ConnectorError::Unsupported(format!(
29                "unsupported generic webhook signature scheme `{other}`; expected one of standard, stripe, github, slack"
30            ))),
31        }
32    }
33
34    pub fn default_timestamp_window(self) -> Option<Duration> {
35        match self {
36            Self::Standard | Self::Stripe | Self::Slack => Some(Duration::minutes(5)),
37            Self::GitHub => None,
38        }
39    }
40
41    pub fn verify(
42        self,
43        event_log: &AnyEventLog,
44        provider: &ProviderId,
45        body: &[u8],
46        headers: &BTreeMap<String, String>,
47        secret: &str,
48        timestamp_window: Option<Duration>,
49        now: OffsetDateTime,
50    ) -> Result<(), ConnectorError> {
51        let style = match self {
52            Self::Standard => HmacSignatureStyle::standard_webhooks(),
53            Self::Stripe => HmacSignatureStyle::stripe(),
54            Self::GitHub => HmacSignatureStyle::github(),
55            Self::Slack => HmacSignatureStyle::slack(),
56        };
57        block_on(verify_hmac_signed(
58            event_log,
59            provider,
60            style,
61            body,
62            headers,
63            secret,
64            timestamp_window.or_else(|| self.default_timestamp_window()),
65            now,
66        ))
67    }
68}