fraiseql_webhooks/signature/
gitlab.rs1use crate::{
6 signature::{SignatureError, constant_time_eq},
7 traits::SignatureVerifier,
8};
9
10pub struct GitLabVerifier;
16
17impl SignatureVerifier for GitLabVerifier {
18 fn name(&self) -> &'static str {
19 "gitlab"
20 }
21
22 fn signature_header(&self) -> &'static str {
23 "X-Gitlab-Token"
24 }
25
26 fn verify(
27 &self,
28 _payload: &[u8],
29 signature: &str,
30 secret: &str,
31 _timestamp: Option<&str>,
32 _url: Option<&str>,
33 ) -> Result<bool, SignatureError> {
34 if secret.is_empty() {
35 return Err(SignatureError::Crypto(
36 "GitLab webhook token must not be empty".to_string(),
37 ));
38 }
39 Ok(constant_time_eq(signature.as_bytes(), secret.as_bytes()))
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 #![allow(clippy::unwrap_used)] use super::*;
49
50 const VERIFIER: GitLabVerifier = GitLabVerifier;
51 const PAYLOAD: &[u8] = b"{\"object_kind\":\"push\"}";
52
53 #[test]
55 fn test_valid_token_accepted() {
56 let secret = "super-secret-token";
57 let result = VERIFIER.verify(PAYLOAD, secret, secret, None, None);
58 assert!(result.unwrap(), "matching token must return true");
59 }
60
61 #[test]
63 fn test_wrong_token_rejected() {
64 let result = VERIFIER.verify(PAYLOAD, "wrong-token", "correct-token", None, None);
65 assert!(!result.unwrap(), "non-matching token must return false");
66 }
67
68 #[test]
70 fn test_empty_secret_returns_error() {
71 let result = VERIFIER.verify(PAYLOAD, "some-token", "", None, None);
72 assert!(result.is_err(), "empty secret must return an error");
73 }
74
75 #[test]
77 fn test_prefix_match_rejected() {
78 let result = VERIFIER.verify(PAYLOAD, "secret", "secret-extra", None, None);
80 assert!(!result.unwrap(), "prefix match must not be accepted");
81 }
82
83 #[test]
85 fn test_payload_ignored() {
86 let secret = "my-token";
87 let r1 = VERIFIER.verify(b"payload-a", secret, secret, None, None).unwrap();
88 let r2 = VERIFIER.verify(b"payload-b", secret, secret, None, None).unwrap();
89 assert!(r1 && r2, "result must not depend on payload content");
90 }
91}