use hmac::{Hmac, KeyInit, Mac};
use serde_json::{Value, json};
use sha2::Sha256;
use crate::message::AlertMessage;
pub const SIGNATURE_HEADER: &str = "X-CodoSEO-Signature";
pub const TIMESTAMP_HEADER: &str = "X-CodoSEO-Timestamp";
pub fn payload(msg: &AlertMessage) -> Value {
let changes: Vec<Value> = msg
.items
.iter()
.map(|i| {
json!({
"severity": i.severity,
"kind": i.kind,
"label": i.kind_label,
"url": i.url,
"before": i.before,
"after": i.after,
})
})
.collect();
json!({
"event": msg.kind.as_str(),
"site": msg.site_domain,
"dashboard_url": msg.site_url,
"crawl_id": msg.crawl_id,
"changes": changes,
"more": msg.more,
})
}
fn mac(secret: &str, timestamp: u64, body: &[u8]) -> Hmac<Sha256> {
let mut mac =
Hmac::<Sha256>::new_from_slice(secret.as_bytes()).expect("HMAC accepts keys of any length");
mac.update(timestamp.to_string().as_bytes());
mac.update(b".");
mac.update(body);
mac
}
pub fn sign(secret: &str, timestamp: u64, body: &[u8]) -> String {
format!(
"sha256={}",
hex::encode(mac(secret, timestamp, body).finalize().into_bytes())
)
}
pub fn verify(secret: &str, timestamp: u64, body: &[u8], signature: &str) -> bool {
let Some(hex_sig) = signature.strip_prefix("sha256=") else {
return false;
};
let Ok(bytes) = hex::decode(hex_sig) else {
return false;
};
mac(secret, timestamp, body).verify_slice(&bytes).is_ok()
}