webhook_router/
handler.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use std::collections::hash_map::HashMap;
8use std::fs::File;
9use std::io;
10use std::path::PathBuf;
11
12use chrono::Utc;
13use hex::FromHex;
14use http::HeaderMap;
15use log::{debug, info};
16use rand::Rng;
17use ring::hmac;
18use serde::Deserialize;
19use serde_json::{json, Value};
20use thiserror::Error;
21
22#[derive(Deserialize, Debug, Clone)]
23struct Filter {
24    kind: String,
25    #[serde(default)]
26    use_header_value: bool,
27
28    #[serde(default)]
29    have_keys: Vec<String>,
30    #[serde(default)]
31    items: HashMap<String, Value>,
32
33    header_value: Option<String>,
34}
35
36impl Filter {
37    fn is_match(&self, header: &Option<&[u8]>, object: &Value) -> bool {
38        if let (&Some(actual), Some(expected)) = (header, &self.header_value) {
39            return actual == expected.as_bytes();
40        }
41
42        for key in &self.have_keys {
43            if object.pointer(key).is_none() {
44                return false;
45            }
46        }
47
48        for (pointer, expected) in &self.items {
49            let matches = object.pointer(pointer).map(|value| value == expected);
50
51            if !matches.unwrap_or(false) {
52                return false;
53            }
54        }
55
56        true
57    }
58}
59
60#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
61pub enum Algorithm {
62    #[serde(rename = "sha1")]
63    Sha1,
64    #[serde(rename = "sha256")]
65    Sha256,
66}
67
68impl Algorithm {
69    fn mac_with(self, key: &[u8]) -> hmac::Key {
70        let algo = match self {
71            Algorithm::Sha1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
72            Algorithm::Sha256 => hmac::HMAC_SHA256,
73        };
74
75        hmac::Key::new(algo, key)
76    }
77}
78
79#[derive(Deserialize, Debug, Clone)]
80#[serde(tag = "type")]
81pub enum Comparison {
82    #[serde(rename = "hmac")]
83    Hmac {
84        algorithm: Algorithm,
85        prefix: String,
86    },
87    #[serde(rename = "token")]
88    Token,
89}
90
91impl Comparison {
92    fn verify(&self, given: &[u8], secret: &str, data: &[u8]) -> bool {
93        match *self {
94            Comparison::Token => given == secret.as_bytes(),
95            Comparison::Hmac {
96                ref algorithm,
97                ref prefix,
98            } => {
99                if given.starts_with(prefix.as_bytes()) {
100                    if let Ok(digest) = Vec::<u8>::from_hex(&given[prefix.len()..]) {
101                        let mac = algorithm.mac_with(secret.as_ref());
102                        hmac::verify(&mac, data, &digest)
103                            .map(|()| true)
104                            .unwrap_or(false)
105                    } else {
106                        false
107                    }
108                } else {
109                    false
110                }
111            },
112        }
113    }
114}
115
116#[derive(Deserialize, Debug, Clone)]
117pub struct Verification {
118    secret_key_lookup: String,
119    verification_header: String,
120    compare: Comparison,
121}
122
123impl Verification {
124    fn verify(&self, headers: &HeaderMap, secret: &str, data: &[u8]) -> bool {
125        headers
126            .get(&self.verification_header)
127            .map_or(false, |value| {
128                self.compare.verify(value.as_bytes(), secret, data)
129            })
130    }
131}
132
133#[derive(Debug, Error)]
134pub enum HandlerError {
135    #[error("create file error: {}", source)]
136    Create {
137        filepath: PathBuf,
138        #[source]
139        source: io::Error,
140    },
141    #[error("create file error: {}", source)]
142    Write {
143        #[source]
144        source: serde_json::Error,
145    },
146}
147
148#[derive(Deserialize, Debug, Clone)]
149pub struct Handler {
150    path: String,
151    filters: Vec<Filter>,
152    header_name: Option<String>,
153
154    verification: Option<Verification>,
155}
156
157impl Handler {
158    pub fn lookup_secret<'a>(&self, secrets: &'a Value, object: &Value) -> Option<&'a str> {
159        self.verification.as_ref().and_then(|verification| {
160            let secret_key = if verification.secret_key_lookup.starts_with('/') {
161                object.pointer(&verification.secret_key_lookup).map(|key| {
162                    key.as_str().map(Into::into).unwrap_or_else(|| {
163                        serde_json::to_string(key).expect("JSON values should be serializable")
164                    })
165                })
166            } else {
167                Some(verification.secret_key_lookup.clone())
168            };
169
170            secret_key
171                .and_then(|key| secrets.get(key))
172                .and_then(Value::as_str)
173        })
174    }
175
176    pub fn verify(&self, headers: &HeaderMap, secret: Option<&str>, data: &[u8]) -> bool {
177        if let Some(ref verification) = self.verification {
178            secret.map_or(false, |secret_value| {
179                verification.verify(headers, secret_value, data)
180            })
181        } else {
182            true
183        }
184    }
185
186    pub fn kind(&self, headers: &HeaderMap, object: &Value) -> Option<String> {
187        let header = self
188            .header_name
189            .as_ref()
190            .and_then(|name| headers.get(name).map(|value| value.as_bytes()));
191
192        for filter in &self.filters {
193            if filter.is_match(&header, object) {
194                info!("matched an event of kind {}", filter.kind);
195
196                if filter.use_header_value {
197                    if let Some(value) = header {
198                        let value_str = String::from_utf8_lossy(value);
199                        return Some(filter.kind.replace("HEADER", &value_str));
200                    }
201                }
202
203                return Some(filter.kind.clone());
204            }
205        }
206
207        None
208    }
209
210    pub fn write_object(&self, kind: &str, object: Value) -> Result<PathBuf, HandlerError> {
211        let rndpart = rand::thread_rng()
212            .sample_iter(&rand::distributions::Alphanumeric)
213            .map(char::from)
214            .take(12)
215            .collect::<String>();
216        let filename = format!("{}-{}.json", Utc::now().to_rfc3339(), rndpart);
217
218        let mut filepath = PathBuf::from(&self.path);
219        filepath.push(filename);
220
221        debug!(
222            "writing an event of kind {} to {}",
223            kind,
224            filepath.display(),
225        );
226
227        let mut fout = File::create(&filepath).map_err(|source| {
228            HandlerError::Create {
229                filepath: filepath.clone(),
230                source,
231            }
232        })?;
233
234        let output = json!({
235            "kind": kind,
236            "data": object,
237        });
238
239        serde_json::to_writer(&mut fout, &output).map_err(|source| {
240            HandlerError::Write {
241                source,
242            }
243        })?;
244
245        Ok(filepath)
246    }
247}
248
249#[cfg(test)]
250mod test {
251    use std::collections::hash_map::HashMap;
252    use std::fs;
253    use std::path::Path;
254
255    use http::{HeaderMap, HeaderValue};
256    use serde_json::{json, Value};
257    use tempfile::TempDir;
258
259    use crate::handler::{Algorithm, Comparison, Filter, Handler, HandlerError, Verification};
260    use crate::test_utils;
261
262    const WEBHOOK_HEADER_NAME: &str = "X-Webhook-Listen-Kind";
263    const WEBHOOK_HEADER_NAME_LOWER: &str = "x-webhook-listen-kind";
264    const VERIFICATION_HEADER: &str = "X-Webhook-Listen-Verification";
265    const VERIFICATION_HEADER_LOWER: &str = "x-webhook-listen-verification";
266
267    fn create_handler<'a, I>(filters: I) -> Handler
268    where
269        I: IntoIterator<Item = &'a Filter>,
270    {
271        Handler {
272            path: String::new(),
273            filters: filters.into_iter().cloned().collect(),
274            header_name: Some(WEBHOOK_HEADER_NAME.into()),
275            verification: None,
276        }
277    }
278
279    fn static_value(value: &'static str) -> HeaderValue {
280        HeaderValue::from_static(value)
281    }
282
283    #[test]
284    fn test_empty_filter() {
285        let handler = create_handler(&[Filter {
286            kind: "empty".into(),
287            use_header_value: false,
288            have_keys: Vec::new(),
289            items: HashMap::new(),
290            header_value: None,
291        }]);
292
293        let empty = json!({});
294        let array = json!({
295            "array": [0],
296        });
297        let array_non_array = json!({
298            "array": 0,
299        });
300        let with_slash = json!({
301            "with/slash": 0,
302        });
303        let with_tilde = json!({
304            "with~tilde": 0,
305        });
306        let string_keys = json!({
307            "dict": {
308                "0": 0,
309            },
310        });
311        let string_keys_missing = json!({
312            "dict": {
313                "1": 0,
314            },
315        });
316        let many_keys = json!({
317            "first_key": 0,
318            "second_key": 0,
319        });
320        let many_keys_missing = json!({
321            "first_key": 0,
322        });
323        let all = json!({
324            "array": [0],
325            "with/slash": 0,
326            "with~tilde": 0,
327            "dict": {
328                "0": 0,
329            },
330            "first_key": 0,
331            "second_key": 0,
332        });
333
334        let headers = HeaderMap::new();
335
336        assert_eq!(handler.kind(&headers, &empty), Some("empty".into()));
337        assert_eq!(handler.kind(&headers, &array), Some("empty".into()));
338        assert_eq!(
339            handler.kind(&headers, &array_non_array),
340            Some("empty".into()),
341        );
342        assert_eq!(handler.kind(&headers, &with_slash), Some("empty".into()));
343        assert_eq!(handler.kind(&headers, &with_tilde), Some("empty".into()));
344        assert_eq!(handler.kind(&headers, &string_keys), Some("empty".into()));
345        assert_eq!(
346            handler.kind(&headers, &string_keys_missing),
347            Some("empty".into()),
348        );
349        assert_eq!(handler.kind(&headers, &many_keys), Some("empty".into()));
350        assert_eq!(
351            handler.kind(&headers, &many_keys_missing),
352            Some("empty".into()),
353        );
354        assert_eq!(handler.kind(&headers, &all), Some("empty".into()));
355    }
356
357    #[test]
358    fn test_have_keys() {
359        let handler = create_handler(&[
360            Filter {
361                kind: "array".into(),
362                use_header_value: false,
363                have_keys: vec!["/array/0".into()],
364                items: HashMap::new(),
365                header_value: None,
366            },
367            Filter {
368                kind: "with_slash".into(),
369                use_header_value: false,
370                have_keys: vec!["/with~1slash".into()],
371                items: HashMap::new(),
372                header_value: None,
373            },
374            Filter {
375                kind: "with_tilde".into(),
376                use_header_value: false,
377                have_keys: vec!["/with~0tilde".into()],
378                items: HashMap::new(),
379                header_value: None,
380            },
381            Filter {
382                kind: "string_keys".into(),
383                use_header_value: false,
384                have_keys: vec!["/dict/0".into()],
385                items: HashMap::new(),
386                header_value: None,
387            },
388            Filter {
389                kind: "many_keys".into(),
390                use_header_value: false,
391                have_keys: vec!["/first_key".into(), "/second_key".into()],
392                items: HashMap::new(),
393                header_value: None,
394            },
395        ]);
396
397        let empty = json!({});
398        let array = json!({
399            "array": [0],
400        });
401        let array_non_array = json!({
402            "array": 0,
403        });
404        let with_slash = json!({
405            "with/slash": 0,
406        });
407        let with_tilde = json!({
408            "with~tilde": 0,
409        });
410        let string_keys = json!({
411            "dict": {
412                "0": 0,
413            },
414        });
415        let string_keys_missing = json!({
416            "dict": {
417                "1": 0,
418            },
419        });
420        let many_keys = json!({
421            "first_key": 0,
422            "second_key": 0,
423        });
424        let many_keys_missing = json!({
425            "first_key": 0,
426        });
427        let all = json!({
428            "array": [0],
429            "with/slash": 0,
430            "with~tilde": 0,
431            "dict": {
432                "0": 0,
433            },
434            "first_key": 0,
435            "second_key": 0,
436        });
437
438        let headers = HeaderMap::new();
439
440        assert_eq!(handler.kind(&headers, &empty), None);
441        assert_eq!(handler.kind(&headers, &array), Some("array".into()));
442        assert_eq!(handler.kind(&headers, &array_non_array), None);
443        assert_eq!(
444            handler.kind(&headers, &with_slash),
445            Some("with_slash".into()),
446        );
447        assert_eq!(
448            handler.kind(&headers, &with_tilde),
449            Some("with_tilde".into()),
450        );
451        assert_eq!(
452            handler.kind(&headers, &string_keys),
453            Some("string_keys".into()),
454        );
455        assert_eq!(handler.kind(&headers, &string_keys_missing), None);
456        assert_eq!(handler.kind(&headers, &many_keys), Some("many_keys".into()));
457        assert_eq!(handler.kind(&headers, &many_keys_missing), None);
458        assert_eq!(handler.kind(&headers, &all), Some("array".into()));
459    }
460
461    #[test]
462    fn test_items() {
463        let handler = create_handler(&[
464            Filter {
465                kind: "multi".into(),
466                use_header_value: false,
467                have_keys: Vec::new(),
468                items: [("/number".into(), json!(1)), ("/null".into(), Value::Null)]
469                    .iter()
470                    .cloned()
471                    .collect(),
472                header_value: None,
473            },
474            Filter {
475                kind: "null".into(),
476                use_header_value: false,
477                have_keys: Vec::new(),
478                items: [("/null".into(), Value::Null)].iter().cloned().collect(),
479                header_value: None,
480            },
481            Filter {
482                kind: "number".into(),
483                use_header_value: false,
484                have_keys: Vec::new(),
485                items: [("/number".into(), json!(0))].iter().cloned().collect(),
486                header_value: None,
487            },
488            Filter {
489                kind: "string".into(),
490                use_header_value: false,
491                have_keys: Vec::new(),
492                items: [("/string".into(), json!("string"))]
493                    .iter()
494                    .cloned()
495                    .collect(),
496                header_value: None,
497            },
498            Filter {
499                kind: "array".into(),
500                use_header_value: false,
501                have_keys: Vec::new(),
502                items: [("/array".into(), json!([0]))].iter().cloned().collect(),
503                header_value: None,
504            },
505            Filter {
506                kind: "dict".into(),
507                use_header_value: false,
508                have_keys: Vec::new(),
509                items: [("/dict".into(), json!({"0": 0}))]
510                    .iter()
511                    .cloned()
512                    .collect(),
513                header_value: None,
514            },
515        ]);
516
517        let empty = json!({});
518        let null = json!({
519            "null": Value::Null,
520        });
521        let null_mismatch = json!({
522            "null": 0,
523        });
524        let number = json!({
525            "number": 0,
526        });
527        let number_mismatch = json!({
528            "number": 1,
529        });
530        let string = json!({
531            "string": "string",
532        });
533        let string_mismatch = json!({
534            "string": "mismatch",
535        });
536        let array = json!({
537            "array": [0],
538        });
539        let array_mismatch = json!({
540            "array": 0,
541        });
542        let dict = json!({
543            "dict": {
544                "0": 0,
545            },
546        });
547        let dict_mismatch = json!({
548            "dict": {
549                "1": 0,
550            },
551        });
552        let multi = json!({
553            "null": Value::Null,
554            "number": 1,
555        });
556        let multi_missing = json!({
557            "number": 1,
558        });
559
560        let headers = HeaderMap::new();
561
562        assert_eq!(handler.kind(&headers, &empty), None);
563        assert_eq!(handler.kind(&headers, &null), Some("null".into()));
564        assert_eq!(handler.kind(&headers, &null_mismatch), None);
565        assert_eq!(handler.kind(&headers, &number), Some("number".into()));
566        assert_eq!(handler.kind(&headers, &number_mismatch), None);
567        assert_eq!(handler.kind(&headers, &string), Some("string".into()));
568        assert_eq!(handler.kind(&headers, &string_mismatch), None);
569        assert_eq!(handler.kind(&headers, &array), Some("array".into()));
570        assert_eq!(handler.kind(&headers, &array_mismatch), None);
571        assert_eq!(handler.kind(&headers, &dict), Some("dict".into()));
572        assert_eq!(handler.kind(&headers, &dict_mismatch), None);
573        assert_eq!(handler.kind(&headers, &multi), Some("multi".into()));
574        assert_eq!(handler.kind(&headers, &multi_missing), None);
575    }
576
577    #[test]
578    fn test_have_keys_and_items() {
579        let handler = create_handler(&[Filter {
580            kind: "match".into(),
581            use_header_value: false,
582            have_keys: vec!["/array".into()],
583            items: [("/null".into(), Value::Null)].iter().cloned().collect(),
584            header_value: None,
585        }]);
586
587        let empty = json!({});
588        let has_keys = json!({
589            "array": 0,
590        });
591        let has_item = json!({
592            "null": Value::Null,
593        });
594        let has_everything = json!({
595            "array": 0,
596            "null": Value::Null,
597        });
598        let has_mismatch = json!({
599            "array": 0,
600            "null": 0,
601        });
602
603        let headers = HeaderMap::new();
604
605        assert_eq!(handler.kind(&headers, &empty), None);
606        assert_eq!(handler.kind(&headers, &has_keys), None);
607        assert_eq!(handler.kind(&headers, &has_item), None);
608        assert_eq!(
609            handler.kind(&headers, &has_everything),
610            Some("match".into()),
611        );
612        assert_eq!(handler.kind(&headers, &has_mismatch), None);
613    }
614
615    #[test]
616    fn test_headers() {
617        let handler = create_handler(&[
618            Filter {
619                kind: "also_keys".into(),
620                use_header_value: false,
621                have_keys: vec!["/array".into()],
622                items: HashMap::new(),
623                header_value: Some("also_keys".into()),
624            },
625            Filter {
626                kind: "multi".into(),
627                use_header_value: false,
628                have_keys: Vec::new(),
629                items: HashMap::new(),
630                header_value: Some("multi".into()),
631            },
632        ]);
633
634        let empty = json!({});
635        let array = json!({
636            "array": [0],
637        });
638
639        let mut headers = HeaderMap::new();
640
641        assert_eq!(handler.kind(&headers, &empty), Some("multi".into()));
642        assert_eq!(handler.kind(&headers, &array), Some("also_keys".into()));
643
644        headers.insert(WEBHOOK_HEADER_NAME, static_value("multi"));
645
646        assert_eq!(handler.kind(&headers, &empty), Some("multi".into()));
647        assert_eq!(handler.kind(&headers, &array), Some("multi".into()));
648
649        headers.insert(WEBHOOK_HEADER_NAME, static_value("also_keys"));
650
651        assert_eq!(handler.kind(&headers, &empty), Some("also_keys".into()));
652        assert_eq!(handler.kind(&headers, &array), Some("also_keys".into()));
653
654        headers.insert(WEBHOOK_HEADER_NAME_LOWER, static_value("also_keys"));
655
656        assert_eq!(handler.kind(&headers, &empty), Some("also_keys".into()));
657        assert_eq!(handler.kind(&headers, &array), Some("also_keys".into()));
658
659        headers.insert(WEBHOOK_HEADER_NAME, static_value("unmatched"));
660
661        assert_eq!(handler.kind(&headers, &empty), None);
662        assert_eq!(handler.kind(&headers, &array), None);
663    }
664
665    #[test]
666    fn test_headers_replaced() {
667        let handler = create_handler(&[
668            Filter {
669                kind: "ignore".into(),
670                use_header_value: true,
671                have_keys: vec!["/have_keys".into()],
672                items: HashMap::new(),
673                header_value: Some("ignore".into()),
674            },
675            Filter {
676                kind: "unreplaced".into(),
677                use_header_value: true,
678                have_keys: vec!["/have_keys".into()],
679                items: HashMap::new(),
680                header_value: Some("unreplaced".into()),
681            },
682            Filter {
683                kind: "unused:HEADER".into(),
684                use_header_value: false,
685                have_keys: vec!["/have_keys".into()],
686                items: HashMap::new(),
687                header_value: Some("unused".into()),
688            },
689            Filter {
690                kind: "catchall:HEADER".into(),
691                use_header_value: true,
692                have_keys: Vec::new(),
693                items: HashMap::new(),
694                header_value: None,
695            },
696        ]);
697
698        let empty = json!({});
699        let data = json!({
700            "have_keys": false,
701        });
702
703        let mut headers = HeaderMap::new();
704
705        assert_eq!(
706            handler.kind(&headers, &empty),
707            Some("catchall:HEADER".into()),
708        );
709        assert_eq!(handler.kind(&headers, &data), Some("ignore".into()));
710
711        headers.insert(WEBHOOK_HEADER_NAME, static_value("ignore"));
712
713        assert_eq!(handler.kind(&headers, &data), Some("ignore".into()));
714
715        headers.insert(WEBHOOK_HEADER_NAME, static_value("unreplaced"));
716
717        assert_eq!(handler.kind(&headers, &data), Some("unreplaced".into()));
718
719        headers.insert(WEBHOOK_HEADER_NAME, static_value("unused"));
720
721        assert_eq!(handler.kind(&headers, &data), Some("unused:HEADER".into()));
722
723        headers.insert(WEBHOOK_HEADER_NAME, static_value("other"));
724
725        assert_eq!(handler.kind(&headers, &data), Some("catchall:other".into()));
726    }
727
728    #[test]
729    fn test_verification_lookup_secret() {
730        let mut handler = create_handler(&[]);
731
732        let no_secrets = json!({});
733        let with_secret = json!({
734            "key": "secret",
735            "123": "secret with int",
736            "literal": "direct lookup",
737        });
738        let data = json!({
739            "secret_key": "key",
740        });
741        let data_non_str = json!({
742            "secret_key": 123,
743        });
744
745        assert_eq!(handler.lookup_secret(&no_secrets, &data), None);
746        assert_eq!(handler.lookup_secret(&no_secrets, &data_non_str), None);
747        assert_eq!(handler.lookup_secret(&with_secret, &data), None);
748        assert_eq!(handler.lookup_secret(&with_secret, &data_non_str), None);
749
750        handler.verification = Some(Verification {
751            secret_key_lookup: "/secret_key".into(),
752            verification_header: VERIFICATION_HEADER.into(),
753            compare: Comparison::Token,
754        });
755
756        assert_eq!(handler.lookup_secret(&no_secrets, &data), None);
757        assert_eq!(handler.lookup_secret(&no_secrets, &data_non_str), None);
758        assert_eq!(handler.lookup_secret(&with_secret, &data), Some("secret"));
759        assert_eq!(
760            handler.lookup_secret(&with_secret, &data_non_str),
761            Some("secret with int"),
762        );
763
764        handler.verification = Some(Verification {
765            secret_key_lookup: "literal".into(),
766            verification_header: VERIFICATION_HEADER.into(),
767            compare: Comparison::Token,
768        });
769
770        assert_eq!(handler.lookup_secret(&no_secrets, &data), None);
771        assert_eq!(handler.lookup_secret(&no_secrets, &data_non_str), None);
772        assert_eq!(
773            handler.lookup_secret(&with_secret, &data),
774            Some("direct lookup"),
775        );
776        assert_eq!(
777            handler.lookup_secret(&with_secret, &data_non_str),
778            Some("direct lookup"),
779        );
780    }
781
782    #[test]
783    fn test_verification_no_header() {
784        let mut handler = create_handler(&[]);
785
786        handler.verification = Some(Verification {
787            secret_key_lookup: "/secret".into(),
788            verification_header: VERIFICATION_HEADER.into(),
789            compare: Comparison::Token,
790        });
791
792        let data = Vec::new();
793        let headers = HeaderMap::new();
794
795        assert!(!handler.verify(&headers, None, &data));
796        assert!(!handler.verify(&headers, Some("secret"), &data));
797    }
798
799    #[test]
800    fn test_no_verification() {
801        let handler = create_handler(&[]);
802
803        let data = Vec::new();
804        let mut headers = HeaderMap::new();
805
806        headers.insert(VERIFICATION_HEADER, static_value("secret"));
807
808        assert!(handler.verify(&headers, None, &data));
809        assert!(handler.verify(&headers, Some("secret"), &data));
810        assert!(handler.verify(&headers, Some("wrong_secret"), &data));
811    }
812
813    #[test]
814    fn test_verification_token() {
815        let mut handler = create_handler(&[]);
816
817        handler.verification = Some(Verification {
818            secret_key_lookup: "/secret".into(),
819            verification_header: VERIFICATION_HEADER.into(),
820            compare: Comparison::Token,
821        });
822
823        let data = Vec::new();
824        let mut headers = HeaderMap::new();
825
826        headers.insert(VERIFICATION_HEADER, static_value("secret"));
827
828        assert!(!handler.verify(&headers, None, &data));
829        assert!(handler.verify(&headers, Some("secret"), &data));
830    }
831
832    #[test]
833    fn test_verification_token_icase() {
834        let mut handler = create_handler(&[]);
835
836        handler.verification = Some(Verification {
837            secret_key_lookup: "/secret".into(),
838            verification_header: VERIFICATION_HEADER.into(),
839            compare: Comparison::Token,
840        });
841
842        let data = Vec::new();
843        let mut headers = HeaderMap::new();
844
845        headers.insert(VERIFICATION_HEADER_LOWER, static_value("secret"));
846
847        assert!(!handler.verify(&headers, None, &data));
848        assert!(handler.verify(&headers, Some("secret"), &data));
849    }
850
851    #[test]
852    fn test_verification_hmac_sha1() {
853        let mut handler = create_handler(&[]);
854
855        handler.verification = Some(Verification {
856            secret_key_lookup: "/secret".into(),
857            verification_header: VERIFICATION_HEADER.into(),
858            compare: Comparison::Hmac {
859                algorithm: Algorithm::Sha1,
860                prefix: String::new(),
861            },
862        });
863
864        let data = Vec::new();
865        let mut headers = HeaderMap::new();
866
867        headers.insert(
868            VERIFICATION_HEADER,
869            static_value("25af6174a0fcecc4d346680a72b7ce644b9a88e8"),
870        );
871
872        assert!(!handler.verify(&headers, None, &data));
873        assert!(handler.verify(&headers, Some("secret"), &data));
874        assert!(!handler.verify(&headers, Some("wrong_secret"), &data));
875    }
876
877    #[test]
878    fn test_verification_hmac_sha256() {
879        let mut handler = create_handler(&[]);
880
881        handler.verification = Some(Verification {
882            secret_key_lookup: "/secret".into(),
883            verification_header: VERIFICATION_HEADER.into(),
884            compare: Comparison::Hmac {
885                algorithm: Algorithm::Sha256,
886                prefix: String::new(),
887            },
888        });
889
890        let data = Vec::new();
891        let mut headers = HeaderMap::new();
892
893        headers.insert(
894            VERIFICATION_HEADER,
895            static_value("f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169"),
896        );
897
898        assert!(!handler.verify(&headers, None, &data));
899        assert!(handler.verify(&headers, Some("secret"), &data));
900        assert!(!handler.verify(&headers, Some("wrong_secret"), &data));
901    }
902
903    #[test]
904    fn test_verification_hmac_prefix() {
905        let mut handler = create_handler(&[]);
906
907        handler.verification = Some(Verification {
908            secret_key_lookup: "/secret".into(),
909            verification_header: VERIFICATION_HEADER.into(),
910            compare: Comparison::Hmac {
911                algorithm: Algorithm::Sha1,
912                prefix: "sha1=".into(),
913            },
914        });
915
916        let data = Vec::new();
917        let mut headers = HeaderMap::new();
918
919        headers.insert(
920            VERIFICATION_HEADER,
921            static_value("sha1=25af6174a0fcecc4d346680a72b7ce644b9a88e8"),
922        );
923
924        assert!(!handler.verify(&headers, None, &data));
925        assert!(handler.verify(&headers, Some("secret"), &data));
926        assert!(!handler.verify(&headers, Some("wrong_secret"), &data));
927    }
928
929    #[test]
930    fn test_verification_hmac_not_hex() {
931        let mut handler = create_handler(&[]);
932
933        handler.verification = Some(Verification {
934            secret_key_lookup: "/secret".into(),
935            verification_header: VERIFICATION_HEADER.into(),
936            compare: Comparison::Hmac {
937                algorithm: Algorithm::Sha1,
938                prefix: "sha1=".into(),
939            },
940        });
941
942        let data = Vec::new();
943        let mut headers = HeaderMap::new();
944
945        headers.insert(
946            VERIFICATION_HEADER,
947            static_value("sha1=notvalidhexadecimaldigitsequenceofdigits"),
948        );
949
950        assert!(!handler.verify(&headers, None, &data));
951        assert!(!handler.verify(&headers, Some("secret"), &data));
952        assert!(!handler.verify(&headers, Some("wrong_secret"), &data));
953    }
954
955    #[test]
956    fn test_verification_hmac_wrong_prefix() {
957        let mut handler = create_handler(&[]);
958
959        handler.verification = Some(Verification {
960            secret_key_lookup: "/secret".into(),
961            verification_header: VERIFICATION_HEADER.into(),
962            compare: Comparison::Hmac {
963                algorithm: Algorithm::Sha1,
964                prefix: "sha12nope=".into(),
965            },
966        });
967
968        let data = Vec::new();
969        let mut headers = HeaderMap::new();
970
971        headers.insert(
972            VERIFICATION_HEADER,
973            static_value("sha1=25af6174a0fcecc4d346680a72b7ce644b9a88e8"),
974        );
975
976        assert!(!handler.verify(&headers, None, &data));
977        assert!(!handler.verify(&headers, Some("secret"), &data));
978        assert!(!handler.verify(&headers, Some("wrong_secret"), &data));
979    }
980
981    fn create_handler_with_dir() -> (Handler, TempDir) {
982        let tempdir = test_utils::create_tempdir();
983
984        let handler = Handler {
985            path: tempdir
986                .path()
987                .to_str()
988                .expect("test path to be valid UTF-8")
989                .into(),
990            filters: Vec::new(),
991            header_name: Some(WEBHOOK_HEADER_NAME.into()),
992            verification: None,
993        };
994
995        (handler, tempdir)
996    }
997
998    fn check_written_object(path: &Path, object: &Value) {
999        let contents = fs::read_to_string(path).unwrap();
1000        let actual: Value = serde_json::from_str(&contents).unwrap();
1001        assert_eq!(&actual, object);
1002    }
1003
1004    #[test]
1005    fn test_write_object_to_dir() {
1006        let (handler, tempdir) = create_handler_with_dir();
1007        let kind = "kind";
1008        let value = json!({
1009            "blah": null,
1010        });
1011
1012        let output = handler.write_object(kind, value.clone()).unwrap();
1013
1014        let expected = json!({
1015            "kind": kind,
1016            "data": value,
1017        });
1018        check_written_object(&output, &expected);
1019
1020        // Explicitly drop the tempdir (also counts as usage).
1021        let _ = tempdir;
1022    }
1023
1024    #[test]
1025    fn test_write_object_to_dir_err() {
1026        let (handler, tempdir_path) = {
1027            let (handler, tempdir) = create_handler_with_dir();
1028            let tempdir_path = tempdir.path().to_path_buf();
1029            (handler, tempdir_path)
1030        };
1031
1032        let kind = "kind";
1033        let value = json!({
1034            "blah": null,
1035        });
1036
1037        let err = handler.write_object(kind, value).unwrap_err();
1038
1039        if let HandlerError::Create {
1040            filepath, ..
1041        } = err
1042        {
1043            assert!(filepath.starts_with(tempdir_path))
1044        } else {
1045            panic!("unexpected error: {:?}", err);
1046        }
1047    }
1048}