harn_vm/connectors/webhook/
variants.rs1use 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}